Actually launching services should work now:

* ServiceSocketMap now maps directly to service_address objects which now have
  gotten a pointer to their owner.
* now uses listen(), and accept() for SOCK_STREAM sockets
* fMinSocket could be wrong.
* use FD_CLOEXEC on all sockets, and the communication pipe.
* removed unused service::type and service::protocol.
* if no family is specified, we now default to AF_INET.
* uses fork()/exec() to actually spawn the servers to not clobber the net_server's
  input/output streams.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19663 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-12-31 16:21:19 +00:00
parent 4684dfec2d
commit c30e35eb9b
2 changed files with 94 additions and 29 deletions

View File

@ -17,12 +17,14 @@
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <new> #include <new>
#include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <vector> #include <vector>
using namespace std; using namespace std;
struct service_address { struct service_address {
struct service *owner;
int socket; int socket;
int family; int family;
int type; int type;
@ -35,8 +37,6 @@ typedef vector<service_address> AddressList;
struct service { struct service {
std::string name; std::string name;
std::string launch; std::string launch;
int type;
int protocol;
uid_t user; uid_t user;
gid_t group; gid_t group;
AddressList addresses; AddressList addresses;
@ -109,9 +109,7 @@ bool
service::operator==(const struct service& other) service::operator==(const struct service& other)
{ {
if (name != other.name if (name != other.name
|| launch != other.launch || launch != other.launch)
|| type != other.type
|| protocol != other.protocol)
return false; return false;
// TODO: compare addresses! // TODO: compare addresses!
@ -137,15 +135,18 @@ Services::Services(const BMessage& services)
return; return;
} }
fListener = spawn_thread(_Listener, "services listener", B_NORMAL_PRIORITY, this); fcntl(fReadPipe, F_SETFD, FD_CLOEXEC);
if (fListener >= B_OK) fcntl(fWritePipe, F_SETFD, FD_CLOEXEC);
resume_thread(fListener);
FD_ZERO(&fSet); FD_ZERO(&fSet);
FD_SET(fReadPipe, &fSet); FD_SET(fReadPipe, &fSet);
_UpdateMaxSocket(fWritePipe); fMinSocket = fWritePipe + 1;
fMinSocket = fMaxSocket; fMaxSocket = fWritePipe + 1;
fListener = spawn_thread(_Listener, "services listener", B_NORMAL_PRIORITY, this);
if (fListener >= B_OK)
resume_thread(fListener);
} }
@ -184,10 +185,12 @@ Services::_NotifyListener(bool quit)
void void
Services::_UpdateMaxSocket(int socket) Services::_UpdateMinMaxSocket(int socket)
{ {
if (socket >= fMaxSocket) if (socket >= fMaxSocket)
fMaxSocket = socket + 1; fMaxSocket = socket + 1;
if (socket < fMinSocket)
fMinSocket = socket;
} }
@ -202,19 +205,17 @@ Services::_StartService(struct service& service)
service_address& address = *iterator; service_address& address = *iterator;
address.socket = socket(address.family, address.type, address.protocol); address.socket = socket(address.family, address.type, address.protocol);
if (address.socket < 0) { if (address.socket < 0
|| bind(address.socket, &address.address, address.address.sa_len) < 0
|| fcntl(address.socket, F_SETFD, FD_CLOEXEC) < 0) {
failed = true; failed = true;
break; break;
} }
if (bind(address.socket, &address.address, address.address.sa_len) < 0) { if (address.type == SOCK_STREAM && listen(address.socket, 50) < 0) {
failed = true; failed = true;
break; break;
} }
fSocketMap[address.socket] = &service;
_UpdateMaxSocket(address.socket);
FD_SET(address.socket, &fSet);
} }
if (failed) { if (failed) {
@ -222,15 +223,17 @@ Services::_StartService(struct service& service)
return errno; return errno;
} }
// add service to maps // add service to maps and activate it
fNameMap[service.name] = &service; fNameMap[service.name] = &service;
iterator = service.addresses.begin(); iterator = service.addresses.begin();
for (; iterator != service.addresses.end(); iterator++) { for (; iterator != service.addresses.end(); iterator++) {
const service_address& address = *iterator; service_address& address = *iterator;
fSocketMap[address.socket] = &service; fSocketMap[address.socket] = &address;
_UpdateMinMaxSocket(address.socket);
FD_SET(address.socket, &fSet);
} }
_NotifyListener(); _NotifyListener();
@ -286,14 +289,17 @@ Services::_ToService(const BMessage& message, struct service*& service)
// TODO: user/group is currently ignored! // TODO: user/group is currently ignored!
// default family/port/protocol/type for all addresses // Default family/port/protocol/type for all addresses
// we default to inet/tcp/port-from-service-name if nothing is specified
const char* string; const char* string;
int32 serviceFamilyIndex; int32 serviceFamilyIndex;
int32 serviceFamily = -1; int32 serviceFamily = -1;
if (message.FindString("family", &string) == B_OK) { if (message.FindString("family", &string) != B_OK)
if (get_family_index(string, serviceFamilyIndex)) string = "inet";
serviceFamily = family_at_index(serviceFamilyIndex);
} if (get_family_index(string, serviceFamilyIndex))
serviceFamily = family_at_index(serviceFamilyIndex);
int32 serviceProtocol; int32 serviceProtocol;
if (message.FindString("protocol", &string) == B_OK) if (message.FindString("protocol", &string) == B_OK)
@ -359,6 +365,7 @@ Services::_ToService(const BMessage& message, struct service*& service)
set_port(familyIndex, serviceAddress.address, port); set_port(familyIndex, serviceAddress.address, port);
serviceAddress.socket = -1; serviceAddress.socket = -1;
serviceAddress.owner = service;
service->addresses.push_back(serviceAddress); service->addresses.push_back(serviceAddress);
} }
@ -379,6 +386,7 @@ Services::_ToService(const BMessage& message, struct service*& service)
set_port(serviceFamilyIndex, serviceAddress.address, servicePort); set_port(serviceFamilyIndex, serviceAddress.address, servicePort);
serviceAddress.socket = -1; serviceAddress.socket = -1;
serviceAddress.owner = service;
service->addresses.push_back(serviceAddress); service->addresses.push_back(serviceAddress);
} }
@ -419,6 +427,43 @@ Services::_Update(const BMessage& services)
} }
status_t
Services::_LaunchService(struct service& service, int socket)
{
printf("LAUNCH: %s\n", service.launch.c_str());
if (fcntl(socket, F_SETFD, 0) < 0) {
// could not clear FD_CLOEXEC on socket
return errno;
}
pid_t child = fork();
if (child == 0) {
// We're the child, replace standard input/output
dup2(socket, STDIN_FILENO);
dup2(socket, STDOUT_FILENO);
dup2(socket, STDERR_FILENO);
close(socket);
// build argument array
const char** args = (const char**)malloc(2 * sizeof(void *));
if (args == NULL)
exit(1);
args[0] = service.launch.c_str();
args[1] = NULL;
if (execv(service.launch.c_str(), (char* const*)args) < 0)
exit(1);
// we'll never trespass here
}
// TODO: make sure child started successfully...
return B_OK;
}
status_t status_t
Services::_Listener() Services::_Listener()
{ {
@ -449,10 +494,28 @@ printf("select returned!\n");
if (iterator == fSocketMap.end()) if (iterator == fSocketMap.end())
continue; continue;
struct service_address& address = *iterator->second;
int socket;
if (address.type == SOCK_STREAM) {
// accept incoming connection
int value = 1;
ioctl(i, FIONBIO, &value);
// make sure we don't wait for the connection
socket = accept(address.socket, NULL, NULL);
value = 0;
ioctl(i, FIONBIO, &value);
if (socket < 0)
continue;
} else
socket = address.socket;
// launch this service's handler // launch this service's handler
struct service* service = iterator->second; _LaunchService(*address.owner, socket);
printf("LAUNCH: %s\n", service->launch.c_str());
} }
} }
return B_OK; return B_OK;

View File

@ -18,8 +18,9 @@
struct service; struct service;
struct service_address;
typedef std::map<std::string, service*> ServiceNameMap; typedef std::map<std::string, service*> ServiceNameMap;
typedef std::map<int, service*> ServiceSocketMap; typedef std::map<int, service_address*> ServiceSocketMap;
class Services : public BHandler { class Services : public BHandler {
@ -33,12 +34,13 @@ class Services : public BHandler {
private: private:
void _NotifyListener(bool quit = false); void _NotifyListener(bool quit = false);
void _UpdateMaxSocket(int socket); void _UpdateMinMaxSocket(int socket);
status_t _StartService(struct service& service); status_t _StartService(struct service& service);
status_t _StopService(struct service& service); status_t _StopService(struct service& service);
status_t _ToService(const BMessage& message, struct service*& service); status_t _ToService(const BMessage& message, struct service*& service);
void _Update(const BMessage& services); void _Update(const BMessage& services);
int32 _CompareServices(struct service& a, struct service& b); int32 _CompareServices(struct service& a, struct service& b);
status_t _LaunchService(struct service& service, int socket);
status_t _Listener(); status_t _Listener();
static status_t _Listener(void* self); static status_t _Listener(void* self);