diff --git a/src/servers/net/AutoconfigLooper.cpp b/src/servers/net/AutoconfigLooper.cpp index efd73dc79b..0c4554fc9d 100644 --- a/src/servers/net/AutoconfigLooper.cpp +++ b/src/servers/net/AutoconfigLooper.cpp @@ -40,6 +40,11 @@ AutoconfigLooper::~AutoconfigLooper() void AutoconfigLooper::_ReadyToRun() { + BMessage interface(kMsgConfigureInterface); + interface.AddString("device", fDevice.String()); + interface.AddInt32("net:status", kStatusPreparing); + fTarget.SendMessage(&interface); + // start with DHCP DHCPClient* client = new DHCPClient(fTarget, fDevice.String()); @@ -57,8 +62,7 @@ AutoconfigLooper::_ReadyToRun() // TODO: have a look at zeroconf // TODO: this could also be done add-on based - BMessage interface; - interface.AddString("device", fDevice.String()); + interface.ReplaceInt32("net:status", kStatusLinkNoConfig); uint8 mac[6]; uint8 last = 56; @@ -80,7 +84,7 @@ AutoconfigLooper::_ReadyToRun() address.AddString("gateway", "192.168.0.254"); interface.AddMessage("address", &address); - fTarget.SendMessage(kMsgConfigureInterface, &interface); + fTarget.SendMessage(&interface); } diff --git a/src/servers/net/DHCPClient.cpp b/src/servers/net/DHCPClient.cpp index 2dbc552ef5..c5e92a3bf2 100644 --- a/src/servers/net/DHCPClient.cpp +++ b/src/servers/net/DHCPClient.cpp @@ -474,6 +474,7 @@ DHCPClient::_Negotiate(dhcp_state state) fConfiguration.MakeEmpty(); fConfiguration.AddString("device", fDevice.String()); + fConfiguration.AddInt32("net:status", kStatusConnected); BMessage address; address.AddString("family", "inet"); diff --git a/src/servers/net/Jamfile b/src/servers/net/Jamfile index ddc948e077..e69ac757bf 100644 --- a/src/servers/net/Jamfile +++ b/src/servers/net/Jamfile @@ -14,6 +14,7 @@ Server net_server : AutoconfigLooper.cpp DHCPClient.cpp Services.cpp + StatusReplicant.cpp : be libnetwork.so $(TARGET_LIBSTDC++) # for PPP diff --git a/src/servers/net/NetServer.cpp b/src/servers/net/NetServer.cpp index 5569b77694..a0c6a96599 100644 --- a/src/servers/net/NetServer.cpp +++ b/src/servers/net/NetServer.cpp @@ -11,12 +11,15 @@ #include "NetServer.h" #include "Services.h" #include "Settings.h" +#include "StatusReplicant.h" #include +#include #include #include #include #include +#include #include #include @@ -36,7 +39,12 @@ #include +const char *kSignature = "application/x-vnd.haiku-net_server"; +static const char *kDeskbarSignature = "application/x-vnd.Be-TSKB"; + + typedef std::map LooperMap; +typedef std::map StatusMap; class NetServer : public BServer { @@ -61,10 +69,15 @@ class NetServer : public BServer { void _ConfigureInterfaces(int socket, BMessage* _missingDevice = NULL); void _BringUpInterfaces(); void _StartServices(); + status_t _AddStatusReplicant(bool force); + void _UpdateDeviceStatus(const char *device, int status); + void _UpdateReplicantStatus(); Settings fSettings; LooperMap fDeviceMap; + StatusMap fStatusMap; BMessenger fServices; + BMessenger fStatusMessenger; }; @@ -236,7 +249,7 @@ get_mac_address(const char* device, uint8* address) NetServer::NetServer(status_t& error) - : BServer("application/x-vnd.haiku-net_server", false, &error) + : BServer(kSignature, false, &error) { } @@ -266,6 +279,13 @@ NetServer::ReadyToRun() fSettings.StartMonitoring(this); _BringUpInterfaces(); _StartServices(); + + // we have to wait for Deskbar to show up before + // adding the status replicant + be_roster->StartWatching(BMessenger(this), B_REQUEST_LAUNCHED); + // but possibly the Deskbar is already running + // so add immediatly + _AddStatusReplicant(true); } @@ -305,18 +325,49 @@ NetServer::MessageReceived(BMessage* message) break; } - // we need a socket to talk to the networking stack - int socket = ::socket(AF_INET, SOCK_DGRAM, 0); - if (socket < 0) - break; + status_t status = B_OK; + if (message->GetInfo("address", NULL) != B_NAME_NOT_FOUND) { + // we need a socket to talk to the networking stack + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) + break; - status_t status = _ConfigureInterface(socket, *message, true); + status = _ConfigureInterface(socket, *message, true); - BMessage reply(B_REPLY); - reply.AddInt32("status", status); - message->SendReply(&reply); + BMessage reply(B_REPLY); + reply.AddInt32("status", status); + message->SendReply(&reply); - close(socket); + close(socket); + } + + if (status == B_OK) { + const char *device; + int32 net_status; + if (message->FindInt32("net:status", &net_status) == B_OK && + message->FindString("device", &device) == B_OK) { + _UpdateDeviceStatus(device, net_status); + } + } + + break; + } + + case B_SOME_APP_LAUNCHED: + { + // check if Deskbar was launched + const char *signature; + if (message->FindString("be:signature", &signature) == B_OK) { + if (strcmp(signature, kDeskbarSignature) == 0) + _AddStatusReplicant(true); + } + break; + } + + case kRegisterStatusReplicant: + { + message->FindMessenger("messenger", &fStatusMessenger); + _UpdateReplicantStatus(); break; } @@ -732,6 +783,13 @@ NetServer::_QuitLooperForDevice(const char* device) iterator->second->Quit(); fDeviceMap.erase(iterator); + + StatusMap::iterator it = fStatusMap.find(device); + if (it != fStatusMap.end()) { + fStatusMap.erase(it); + _UpdateReplicantStatus(); + } + return true; } @@ -877,6 +935,67 @@ NetServer::_StartServices() } +status_t +NetServer::_AddStatusReplicant(bool force) +{ + BDeskbar deskbar; + status_t status; + + if (deskbar.HasItem(kStatusReplicant)) { + if (force) { + status = deskbar.RemoveItem(kStatusReplicant); + if (status != B_OK) + return status; + } else { + return B_OK; + } + } + + StatusReplicant *replicant = new StatusReplicant(); + status = deskbar.AddItem(replicant); + delete replicant; + + return status; +} + + +void +NetServer::_UpdateDeviceStatus(const char *device, int status) +{ + StatusMap::iterator it = fStatusMap.find(device); + if (it != fStatusMap.end() && it->second == status) + return; + + fStatusMap[device] = status; + _UpdateReplicantStatus(); +} + + +void +NetServer::_UpdateReplicantStatus() +{ + if (!fStatusMessenger.IsValid()) + return; + + int prevaling = kStatusUnknown; + + BMessage message(kStatusUpdate); + + for (StatusMap::iterator it = fStatusMap.begin(); + it != fStatusMap.end(); ++it) { + if (it->second > prevaling) + prevaling = it->second; + + message.AddString("intf:name", it->first.c_str()); + message.AddInt32("intf:status", it->second); + } + + message.AddInt32("net:status", prevaling); + + fStatusMessenger.SendMessage(&message); +} + + // #pragma mark - diff --git a/src/servers/net/NetServer.h b/src/servers/net/NetServer.h index 69e48fbc7d..f161fd8bc6 100644 --- a/src/servers/net/NetServer.h +++ b/src/servers/net/NetServer.h @@ -15,6 +15,17 @@ static const uint32 kMsgConfigureInterface = 'COif'; +extern const char *kSignature; + +enum { + kStatusUnknown = 0, + kStatusNoLink, + kStatusLinkNoConfig, + kStatusConnected, + kStatusPreparing, + + kStatusCount +}; extern bool get_family_index(const char* name, int32& familyIndex); extern int family_at_index(int32 index); diff --git a/src/servers/net/StatusReplicant.cpp b/src/servers/net/StatusReplicant.cpp new file mode 100644 index 0000000000..291f889985 --- /dev/null +++ b/src/servers/net/StatusReplicant.cpp @@ -0,0 +1,278 @@ +/* + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Hugo Santos, hugosantos@gmail.com + */ + +#include "NetServer.h" +#include "StatusReplicant.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +const char *kStatusReplicant = "NetStatusView"; +static const uint32 kShowConfiguration = 'shcf'; + +static const uint8 kStatusColors[kStatusCount][3] = { + { 0, 0, 0 }, // Unknown + { 255, 0, 0 }, // NoLink + { 0, 0, 255 }, // LinkNoConfig + { 0, 255, 0 }, // Connected + { 255, 255, 0 }, // Preparing +}; + +struct information_entry { + const char *label; + int ioc; +}; + +static const information_entry kInformationEntries[] = { + { "Address", SIOCGIFADDR }, + { "Broadcast", SIOCGIFBRDADDR }, + { "Netmask", SIOCGIFNETMASK }, + { NULL } +}; + + +static const char *kStatusDescriptions[] = { + "Unknown", + "No Link", + "No stateful configuration", + "Ready", + "Configuring", +}; + + +StatusReplicant::StatusReplicant() + : BView(BRect(0, 0, 15, 15), kStatusReplicant, B_FOLLOW_ALL, 0), + fPopup("", false, false) +{ +} + + +StatusReplicant::StatusReplicant(BMessage *message) + : BView(message), fPopup("", false, false) +{ + // TODO: Load status bitmaps from resources + for (int i = 0; i < kStatusCount; i++) + fBitmaps[i] = NULL; +} + + +StatusReplicant::~StatusReplicant() +{ +} + + +StatusReplicant * +StatusReplicant::Instantiate(BMessage *data) +{ + if (validate_instantiation(data, kStatusReplicant)) + return new StatusReplicant(data); + return NULL; +} + + +status_t +StatusReplicant::Archive(BMessage *data, bool deep) const +{ + BView::Archive(data, deep); + data->AddString("add_on", kSignature); + data->AddString("class", kStatusReplicant); + return B_OK; +} + + +void +StatusReplicant::AttachedToWindow() +{ + BMessage message(kRegisterStatusReplicant); + message.AddMessenger("messenger", BMessenger(this)); + + if (BMessenger(kSignature).SendMessage(&message) != B_OK) { + // TODO: Remove myself from Deskbar + } + + ChangeStatus(kStatusUnknown); + PrepareMenu(InterfaceStatusList()); +} + + +void +StatusReplicant::MessageReceived(BMessage *message) +{ + switch (message->what) { + case kStatusUpdate: + UpdateFromMessage(message); + break; + case kShowConfiguration: + ShowConfiguration(message); + break; + default: + BView::MessageReceived(message); + break; + } +} + + +void +StatusReplicant::MouseDown(BPoint point) +{ + uint32 buttons; + BPoint where; + + GetMouse(&where, &buttons, true); + where = ConvertToScreen(point); + + fPopup.SetTargetForItems(this); + fPopup.Go(where, true, true); +} + + +void +StatusReplicant::UpdateFromMessage(BMessage *message) +{ + int32 newStatus, intfStatus; + + if (message->FindInt32("net:status", &newStatus) != B_OK + || newStatus < 0 || newStatus >= kStatusCount) + return; + + InterfaceStatusList status; + int index = 0; + while (message->FindInt32("intf:status", index, &intfStatus) == B_OK) { + if (intfStatus < 0 || intfStatus >= kStatusCount) + break; + + const char *name; + if (message->FindString("intf:name", index, &name) != B_OK) + break; + + status.push_back(InterfaceStatus(name, intfStatus)); + + index++; + } + + ChangeStatus(newStatus); + PrepareMenu(status); +} + + +void +StatusReplicant::ShowConfiguration(BMessage *message) +{ + const char *device; + if (message->FindString("intf:name", &device) != B_OK) + return; + + int32 status; + if (message->FindInt32("intf:status", &status) != B_OK) + return; + + if (status != kStatusConnected && status != kStatusLinkNoConfig) + return; + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + return; + + if (strlen(device) > IF_NAMESIZE) + return; + + ifreq request; + memset(&request, 0, sizeof(request)); + strcpy(request.ifr_name, device); + + string text = device; + text += " information:\n"; + size_t boldLength = text.size(); + + for (int i = 0; kInformationEntries[i].label; i++) { + if (ioctl(sock, kInformationEntries[i].ioc, &request, + sizeof(request)) < 0) { + close(sock); + return; + } + + char address[32]; + if (inet_ntop(AF_INET, &((sockaddr_in *)&request.ifr_addr)->sin_addr, + address, sizeof(address)) == NULL) { + close(sock); + return; + } + + text += "\n"; + text += kInformationEntries[i].label; + text += ": "; + text += address; + } + + close(sock); + + BAlert *info = new BAlert(device, text.c_str(), "Ok"); + BTextView *view = info->TextView(); + BFont font; + + view->SetStylable(true); + view->GetFont(&font); + font.SetSize(12); + font.SetFace(B_BOLD_FACE); + view->SetFontAndColor(0, boldLength, &font); + + info->Go(NULL); +} + + +void +StatusReplicant::PrepareMenu(const InterfaceStatusList &status) +{ + while (fPopup.RemoveItem((int32)0)); + + if (status.empty()) { + fPopup.AddItem(new BMenuItem("No interfaces available.", NULL)); + fPopup.ItemAt(0)->SetEnabled(false); + } else { + for (InterfaceStatusList::const_iterator i = status.begin(); + i != status.end(); ++i) { + std::string label = i->first; + label += ": "; + label += kStatusDescriptions[i->second]; + + BMessage *message = new BMessage(kShowConfiguration); + message->AddString("intf:name", i->first.c_str()); + message->AddInt32("intf:status", i->second); + + BMenuItem *item = new BMenuItem(label.c_str(), message); + if (i->second != kStatusConnected && + i->second != kStatusLinkNoConfig) + item->SetEnabled(false); + fPopup.AddItem(item); + } + } +} + + +void +StatusReplicant::ChangeStatus(int newStatus) +{ + if (fBitmaps[newStatus]) { + SetViewColor(Parent()->ViewColor()); + SetViewBitmap(fBitmaps[newStatus]); + } else { + ClearViewBitmap(); + SetViewColor(kStatusColors[newStatus][0], + kStatusColors[newStatus][1], + kStatusColors[newStatus][2]); + } + + Invalidate(); +} diff --git a/src/servers/net/StatusReplicant.h b/src/servers/net/StatusReplicant.h new file mode 100644 index 0000000000..4ac22ce65a --- /dev/null +++ b/src/servers/net/StatusReplicant.h @@ -0,0 +1,52 @@ +/* + * Copyright 2007, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Hugo Santos, hugosantos@gmail.com + */ + +#ifndef _STATUS_REPLICANT_H +#define _STATUS_REPLICANT_H + +#include "NetServer.h" + +#include +#include + +#include +#include +#include + +static const uint32 kRegisterStatusReplicant = 'rnsr'; +static const uint32 kStatusUpdate = 'stup'; +extern const char *kStatusReplicant; + +class StatusReplicant : public BView { +public: + StatusReplicant(); + StatusReplicant(BMessage *); + virtual ~StatusReplicant(); + + static StatusReplicant *Instantiate(BMessage *); + status_t Archive(BMessage *, bool deep) const; + + void AttachedToWindow(); + void MessageReceived(BMessage *); + + void MouseDown(BPoint point); + +private: + typedef std::pair InterfaceStatus; + typedef std::vector InterfaceStatusList; + + void UpdateFromMessage(BMessage *); + void ShowConfiguration(BMessage *); + void PrepareMenu(const InterfaceStatusList &); + void ChangeStatus(int newStatus); + + BPopUpMenu fPopup; + BBitmap *fBitmaps[kStatusCount]; +}; + +#endif