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:
parent
c8c228ca64
commit
c42ca72638
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user