From c42ca72638425e7b8745426654a7f6d0de054464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 28 Dec 2006 13:58:58 +0000 Subject: [PATCH] Work-in-progress on the "services" implementation: it now reads the configuration message, parses it, and stops/starts the services as needed - it doesn't launch any actual servers yet, though. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19645 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/servers/net/NetServer.cpp | 66 +++++++-- src/servers/net/NetServer.h | 7 + src/servers/net/Services.cpp | 263 ++++++++++++++++++++++++++++++++-- src/servers/net/Services.h | 9 ++ src/servers/net/Settings.cpp | 5 + 5 files changed, 331 insertions(+), 19 deletions(-) diff --git a/src/servers/net/NetServer.cpp b/src/servers/net/NetServer.cpp index 13e2677ef8..45a30ccb00 100644 --- a/src/servers/net/NetServer.cpp +++ b/src/servers/net/NetServer.cpp @@ -70,17 +70,23 @@ struct address_family { const char* name; const char* identifiers[4]; bool (*parse_address)(const char* string, sockaddr* _address); + void (*set_any_address)(sockaddr* address); + void (*set_port)(sockaddr* address, int32 port); }; // AF_INET family static bool inet_parse_address(const char* string, sockaddr* address); +static void inet_set_any_address(sockaddr* address); +static void inet_set_port(sockaddr* address, int32 port); static const address_family kFamilies[] = { { AF_INET, "inet", {"AF_INET", "inet", "ipv4", NULL}, - inet_parse_address + inet_parse_address, + inet_set_any_address, + inet_set_port }, { -1, NULL, {NULL}, NULL } }; @@ -95,7 +101,7 @@ inet_parse_address(const char* string, sockaddr* _address) return false; sockaddr_in& address = *(sockaddr_in *)_address; - address.sin_family = AF_INET; + address.sin_family = AF_INET; address.sin_len = sizeof(struct sockaddr_in); address.sin_port = 0; address.sin_addr = inetAddress; @@ -105,10 +111,30 @@ inet_parse_address(const char* string, sockaddr* _address) } +void +inet_set_any_address(sockaddr* _address) +{ + sockaddr_in& address = *(sockaddr_in*)_address; + address.sin_family = AF_INET; + address.sin_len = sizeof(struct sockaddr_in); + address.sin_port = 0; + address.sin_addr.s_addr = INADDR_ANY; + memset(&address.sin_zero[0], 0, sizeof(address.sin_zero)); +} + + +void +inet_set_port(sockaddr* _address, int32 port) +{ + sockaddr_in& address = *(sockaddr_in*)_address; + address.sin_port = port; +} + + // #pragma mark - -static bool +bool get_family_index(const char* name, int32& familyIndex) { for (int32 i = 0; kFamilies[i].family >= 0; i++) { @@ -127,7 +153,14 @@ get_family_index(const char* name, int32& familyIndex) } -static bool +int +family_at_index(int32 index) +{ + return kFamilies[index].family; +} + + +bool parse_address(int32 familyIndex, const char* argument, struct sockaddr& address) { if (argument == NULL) @@ -137,6 +170,20 @@ parse_address(int32 familyIndex, const char* argument, struct sockaddr& address) } +void +set_any_address(int32 familyIndex, struct sockaddr& address) +{ + kFamilies[familyIndex].set_any_address(&address); +} + + +void +set_port(int32 familyIndex, struct sockaddr& address, int32 port) +{ + kFamilies[familyIndex].set_port(&address, port); +} + + bool prepare_request(ifreq& request, const char* name) { @@ -361,8 +408,8 @@ NetServer::_ConfigureInterface(int socket, BMessage& interface, bool fromMessage } int familySocket = socket; - if (kFamilies[familyIndex].family != AF_INET) - socket = ::socket(kFamilies[familyIndex].family, SOCK_DGRAM, 0); + if (family_at_index(familyIndex) != AF_INET) + socket = ::socket(family_at_index(familyIndex), SOCK_DGRAM, 0); if (socket < 0) { // the family is not available in this environment continue; @@ -465,7 +512,7 @@ NetServer::_ConfigureInterface(int socket, BMessage& interface, bool fromMessage } int32 currentFlags = request.ifr_flags; - if (!hasMask && hasAddress && kFamilies[familyIndex].family == AF_INET + if (!hasMask && hasAddress && family_at_index(familyIndex) == AF_INET && ioctl(familySocket, SIOCGIFNETMASK, &request, sizeof(struct ifreq)) == 0 && request.ifr_mask.sa_family == AF_UNSPEC) { // generate standard netmask if it doesn't have one yet @@ -499,7 +546,7 @@ NetServer::_ConfigureInterface(int socket, BMessage& interface, bool fromMessage } if (!hasBroadcast && hasAddress && (currentFlags & IFF_BROADCAST) - && kFamilies[familyIndex].family == AF_INET + && family_at_index(familyIndex) == AF_INET && ioctl(familySocket, SIOCGIFBRDADDR, &request, sizeof(struct ifreq)) == 0 && request.ifr_mask.sa_family == AF_UNSPEC) { // generate standard broadcast address if it doesn't have one yet @@ -683,6 +730,9 @@ NetServer::_BringUpInterfaces() _ConfigureInterface(socket, interface); } + // TODO: also check if the networking driver is correctly initialized! + // (and check for other devices to take over its configuration) + if (!_TestForInterface(socket, "/dev/net/")) { // there is no driver configured - see if there is one and try to use it _ConfigureDevices(socket, "/dev/net"); diff --git a/src/servers/net/NetServer.h b/src/servers/net/NetServer.h index fd3f11d355..69e48fbc7d 100644 --- a/src/servers/net/NetServer.h +++ b/src/servers/net/NetServer.h @@ -16,6 +16,13 @@ static const uint32 kMsgConfigureInterface = 'COif'; +extern bool get_family_index(const char* name, int32& familyIndex); +extern int family_at_index(int32 index); +extern bool parse_address(int32 familyIndex, const char* argument, + struct sockaddr& address); +extern void set_any_address(int32 familyIndex, struct sockaddr& address); +extern void set_port(int32 familyIndex, struct sockaddr& address, int32 port); + extern bool prepare_request(ifreq& request, const char* name); extern status_t get_mac_address(const char* device, uint8* address); diff --git a/src/servers/net/Services.cpp b/src/servers/net/Services.cpp index 2a61edf3ce..c51b409b02 100644 --- a/src/servers/net/Services.cpp +++ b/src/servers/net/Services.cpp @@ -8,10 +8,14 @@ #include "Services.h" +#include "NetServer.h" #include "Settings.h" #include +#include +#include +#include #include #include #include @@ -21,7 +25,8 @@ using namespace std; struct service_address { int socket; int family; - uint16 port; + int type; + int protocol; sockaddr address; }; @@ -32,7 +37,6 @@ struct service { std::string launch; int type; int protocol; - uint16 port; uid_t user; gid_t group; AddressList addresses; @@ -43,6 +47,45 @@ struct service { }; +int +parse_type(const char* string) +{ + if (!strcasecmp(string, "stream")) + return SOCK_STREAM; + + return SOCK_DGRAM; +} + + +int +parse_protocol(const char* string) +{ + struct protoent* proto = getprotobyname(string); + if (proto == NULL) + return IPPROTO_TCP; + + return proto->p_proto; +} + + +int +type_for_protocol(int protocol) +{ + // default determined by protocol + switch (protocol) { + case IPPROTO_TCP: + return SOCK_STREAM; + + case IPPROTO_UDP: + default: + return SOCK_DGRAM; + } +} + + +// #pragma mark - + + service::~service() { // close all open sockets @@ -68,8 +111,7 @@ service::operator==(const struct service& other) if (name != other.name || launch != other.launch || type != other.type - || protocol != other.protocol - || port != other.port) + || protocol != other.protocol) return false; // TODO: compare addresses! @@ -82,14 +124,28 @@ service::operator==(const struct service& other) Services::Services(const BMessage& services) : - fUpdate(0) + fListener(-1), + fUpdate(0), + fMaxSocket(0) { _Update(services); + // setup pipe to communicate with the listener thread - as the listener + // blocks on select(), we need a mechanism to interrupt it + if (pipe(&fReadPipe) < 0) { + fReadPipe = -1; + return; + } + fListener = spawn_thread(_Listener, "services listener", B_NORMAL_PRIORITY, this); if (fListener >= B_OK) resume_thread(fListener); - + + FD_ZERO(&fSet); + FD_SET(fReadPipe, &fSet); + + _UpdateMaxSocket(fWritePipe); + fMinSocket = fMaxSocket; } @@ -110,7 +166,7 @@ void Services::MessageReceived(BMessage* message) { switch (message->what) { - case kMsgServiceSettingsUpdated: + case kMsgUpdateServices: _Update(*message); break; @@ -120,20 +176,65 @@ Services::MessageReceived(BMessage* message) } +void +Services::_NotifyListener(bool quit) +{ + write(fWritePipe, quit ? "q" : "u", 1); +} + + +void +Services::_UpdateMaxSocket(int socket) +{ + if (socket >= fMaxSocket) + fMaxSocket = socket + 1; +} + + status_t Services::_StartService(struct service& service) { + // create socket + + bool failed = false; + AddressList::iterator iterator = service.addresses.begin(); + for (; iterator != service.addresses.end(); iterator++) { + service_address& address = *iterator; + + address.socket = socket(address.family, address.type, address.protocol); + if (address.socket < 0) { + failed = true; + break; + } + + if (bind(address.socket, &address.address, address.address.sa_len) < 0) { + failed = true; + break; + } + + fSocketMap[address.socket] = &service; + _UpdateMaxSocket(address.socket); + FD_SET(address.socket, &fSet); + } + + if (failed) { + // open sockets will be closed when the service is deleted + return errno; + } + // add service to maps fNameMap[service.name] = &service; - AddressList::const_iterator iterator = service.addresses.begin(); + iterator = service.addresses.begin(); for (; iterator != service.addresses.end(); iterator++) { const service_address& address = *iterator; fSocketMap[address.socket] = &service; } + _NotifyListener(); +printf("Starting service '%s'\n", service.name.c_str()); return B_OK; } @@ -155,6 +256,9 @@ Services::_StopService(struct service& service) ServiceSocketMap::iterator socketIterator = fSocketMap.find(address.socket); if (socketIterator != fSocketMap.end()) fSocketMap.erase(socketIterator); + + close(address.socket); + FD_CLR(address.socket, &fSet); } } @@ -180,7 +284,104 @@ Services::_ToService(const BMessage& message, struct service*& service) service->name = name; service->launch = launch; - // TODO: fill in other fields + // TODO: user/group is currently ignored! + + // default family/port/protocol/type for all addresses + const char* string; + int32 serviceFamilyIndex; + int32 serviceFamily = -1; + if (message.FindString("family", &string) == B_OK) { + if (get_family_index(string, serviceFamilyIndex)) + serviceFamily = family_at_index(serviceFamilyIndex); + } + + int32 serviceProtocol; + if (message.FindString("protocol", &string) == B_OK) + serviceProtocol = parse_protocol(string); + else { + string = "tcp"; + // we set 'string' here for an eventual call to getservbyname() below + serviceProtocol = IPPROTO_TCP; + } + + int32 servicePort; + if (message.FindInt32("port", &servicePort) != B_OK) { + struct servent* servent = getservbyname(name, string); + if (servent != NULL) + servicePort = servent->s_port; + else + servicePort = -1; + } + + int32 serviceType = -1; + if (message.FindString("type", &string) == B_OK) { + serviceType = parse_type(string); + } else { + serviceType = type_for_protocol(serviceProtocol); + } + + BMessage address; + int32 i = 0; + for (; message.FindMessage("address", i, &address) == B_OK; i++) { + // TODO: dump problems in the settings to syslog + service_address serviceAddress; + if (address.FindString("family", &string) != B_OK) + continue; + + int32 familyIndex; + if (!get_family_index(string, familyIndex)) + continue; + + serviceAddress.family = family_at_index(familyIndex); + + if (address.FindString("protocol", &string) == B_OK) + serviceAddress.protocol = parse_protocol(string); + else + serviceAddress.protocol = serviceProtocol; + + if (message.FindString("type", &string) == B_OK) + serviceAddress.type = parse_type(string); + else if (serviceAddress.protocol != serviceProtocol) + serviceAddress.type = type_for_protocol(serviceAddress.protocol); + else + serviceAddress.type = serviceType; + + if (address.FindString("address", &string) == B_OK) { + if (!parse_address(familyIndex, string, serviceAddress.address)) + continue; + } else + set_any_address(familyIndex, serviceAddress.address); + + int32 port; + if (address.FindInt32("port", &port) != B_OK) + port = servicePort; + + set_port(familyIndex, serviceAddress.address, port); + serviceAddress.socket = -1; + + service->addresses.push_back(serviceAddress); + } + + if (i == 0 && (serviceFamily < 0 || servicePort < 0)) { + // no address specified + delete service; + return B_BAD_VALUE; + } + + if (i == 0) { + // no address specified, but family/port were given; add empty address + service_address serviceAddress; + serviceAddress.family = serviceFamily; + serviceAddress.type = serviceType; + serviceAddress.protocol = serviceProtocol; + + set_any_address(serviceFamilyIndex, serviceAddress.address); + set_port(serviceFamilyIndex, serviceAddress.address, servicePort); + serviceAddress.socket = -1; + + service->addresses.push_back(serviceAddress); + } + return B_OK; } @@ -218,9 +419,49 @@ Services::_Update(const BMessage& services) } -/*static*/ status_t -Services::_Listener(void* self) +status_t +Services::_Listener() { + while (true) { + fLock.Lock(); + fd_set set = fSet; + fLock.Unlock(); + + if (select(fMaxSocket, &set, NULL, NULL, NULL) < 0) { + // sleep a bit before trying again + snooze(1000000LL); + } + +printf("select returned!\n"); + if (FD_ISSET(fReadPipe, &set)) { + char command; + if (read(fReadPipe, &command, 1) == 1 && command == 'q') + break; + } + + BAutolock locker(fLock); + + for (int i = fMinSocket; i < fMaxSocket; i++) { + if (!FD_ISSET(i, &set)) + continue; + + ServiceSocketMap::iterator iterator = fSocketMap.find(i); + if (iterator == fSocketMap.end()) + continue; + + // launch this service's handler + + struct service* service = iterator->second; + printf("LAUNCH: %s\n", service->launch.c_str()); + } + } return B_OK; } + +/*static*/ status_t +Services::_Listener(void* _self) +{ + Services* self = (Services*)_self; + return self->_Listener(); +} diff --git a/src/servers/net/Services.h b/src/servers/net/Services.h index 56eeb210a5..152db43fad 100644 --- a/src/servers/net/Services.h +++ b/src/servers/net/Services.h @@ -14,6 +14,7 @@ #include #include +#include struct service; @@ -31,11 +32,14 @@ class Services : public BHandler { virtual void MessageReceived(BMessage* message); private: + void _NotifyListener(bool quit = false); + void _UpdateMaxSocket(int socket); status_t _StartService(struct service& service); status_t _StopService(struct service& service); status_t _ToService(const BMessage& message, struct service*& service); void _Update(const BMessage& services); int32 _CompareServices(struct service& a, struct service& b); + status_t _Listener(); static status_t _Listener(void* self); thread_id fListener; @@ -43,6 +47,11 @@ class Services : public BHandler { ServiceNameMap fNameMap; ServiceSocketMap fSocketMap; int32 fUpdate; + int fReadPipe; + int fWritePipe; + int fMinSocket; + int fMaxSocket; + fd_set fSet; }; const static uint32 kMsgUpdateServices = 'srvU'; diff --git a/src/servers/net/Settings.cpp b/src/servers/net/Settings.cpp index 106abc1cce..81d985ce18 100644 --- a/src/servers/net/Settings.cpp +++ b/src/servers/net/Settings.cpp @@ -50,6 +50,8 @@ const static settings_template kInterfacesTemplate[] = { const static settings_template kServiceAddressTemplate[] = { {B_STRING_TYPE, "family", NULL}, + {B_STRING_TYPE, "type", NULL}, + {B_STRING_TYPE, "protocol", NULL}, {B_STRING_TYPE, "address", NULL}, {B_INT32_TYPE, "port", NULL}, {0, NULL, NULL} @@ -61,6 +63,9 @@ const static settings_template kServiceTemplate[] = { {B_STRING_TYPE, "user", NULL}, {B_STRING_TYPE, "group", NULL}, {B_STRING_TYPE, "launch", NULL}, + {B_STRING_TYPE, "family", NULL}, + {B_STRING_TYPE, "type", NULL}, + {B_STRING_TYPE, "protocol", NULL}, {B_INT32_TYPE, "port", NULL}, {0, NULL, NULL} };