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
This commit is contained in:
Axel Dörfler 2006-12-28 13:58:58 +00:00
parent c8c228ca64
commit c42ca72638
5 changed files with 331 additions and 19 deletions

View File

@ -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");

View File

@ -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);

View File

@ -8,10 +8,14 @@
#include "Services.h"
#include "NetServer.h"
#include "Settings.h"
#include <Autolock.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <new>
#include <sys/socket.h>
#include <vector>
@ -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();
}

View File

@ -14,6 +14,7 @@
#include <map>
#include <string>
#include <sys/select.h>
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';

View File

@ -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}
};