btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / servers / net / Services.cpp
blob453f26f388ba8e0a158fa05eeb4ae960b2b634a4
1 /*
2 * Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 */
10 #include "Services.h"
12 #include <new>
14 #include <errno.h>
15 #include <netinet/in.h>
16 #include <stdlib.h>
17 #include <strings.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
21 #include <Autolock.h>
22 #include <NetworkAddress.h>
23 #include <NetworkSettings.h>
25 #include <NetServer.h>
28 using namespace std;
29 using namespace BNetworkKit;
32 struct service_connection {
33 struct service* owner;
34 int socket;
35 BNetworkServiceAddressSettings address;
37 //service_connection() : owner(NULL), socket(-1) {}
39 int Family() const { return address.Family(); }
40 int Protocol() const { return address.Protocol(); }
41 int Type() const { return address.Type(); }
42 const BNetworkAddress& Address() const { return address.Address(); }
44 bool operator==(const struct service_connection& other) const;
47 typedef std::vector<service_connection> ConnectionList;
48 typedef std::vector<std::string> StringList;
50 struct service {
51 std::string name;
52 StringList arguments;
53 uid_t user;
54 gid_t group;
55 ConnectionList connections;
56 uint32 update;
57 bool stand_alone;
58 pid_t process;
60 ~service();
61 bool operator!=(const struct service& other) const;
62 bool operator==(const struct service& other) const;
66 // #pragma mark -
69 bool
70 service_connection::operator==(const struct service_connection& other) const
72 return address == other.address;
76 // #pragma mark -
79 service::~service()
81 // close all open sockets
82 ConnectionList::const_iterator iterator = connections.begin();
83 for (; iterator != connections.end(); iterator++) {
84 const service_connection& connection = *iterator;
86 close(connection.socket);
91 bool
92 service::operator!=(const struct service& other) const
94 return !(*this == other);
98 bool
99 service::operator==(const struct service& other) const
101 if (name != other.name
102 || arguments.size() != other.arguments.size()
103 || connections.size() != other.connections.size()
104 || stand_alone != other.stand_alone)
105 return false;
107 // Compare arguments
109 for(size_t i = 0; i < arguments.size(); i++) {
110 if (arguments[i] != other.arguments[i])
111 return false;
114 // Compare connections
116 ConnectionList::const_iterator iterator = connections.begin();
117 for (; iterator != connections.end(); iterator++) {
118 const service_connection& connection = *iterator;
120 // Find address in other addresses
122 ConnectionList::const_iterator otherIterator
123 = other.connections.begin();
124 for (; otherIterator != other.connections.end(); otherIterator++) {
125 if (connection == *otherIterator)
126 break;
129 if (otherIterator == other.connections.end())
130 return false;
133 return true;
137 // #pragma mark -
140 Services::Services(const BMessage& services)
142 fListener(-1),
143 fUpdate(0),
144 fMaxSocket(0)
146 // setup pipe to communicate with the listener thread - as the listener
147 // blocks on select(), we need a mechanism to interrupt it
148 if (pipe(&fReadPipe) < 0) {
149 fReadPipe = -1;
150 return;
153 fcntl(fReadPipe, F_SETFD, FD_CLOEXEC);
154 fcntl(fWritePipe, F_SETFD, FD_CLOEXEC);
156 FD_ZERO(&fSet);
157 FD_SET(fReadPipe, &fSet);
159 fMinSocket = fWritePipe + 1;
160 fMaxSocket = fWritePipe + 1;
162 _Update(services);
164 fListener = spawn_thread(_Listener, "services listener", B_NORMAL_PRIORITY,
165 this);
166 if (fListener >= B_OK)
167 resume_thread(fListener);
171 Services::~Services()
173 wait_for_thread(fListener, NULL);
175 close(fReadPipe);
176 close(fWritePipe);
178 // stop all services
180 while (!fNameMap.empty()) {
181 _StopService(fNameMap.begin()->second);
186 status_t
187 Services::InitCheck() const
189 return fListener >= B_OK ? B_OK : fListener;
193 void
194 Services::MessageReceived(BMessage* message)
196 switch (message->what) {
197 case kMsgUpdateServices:
198 _Update(*message);
199 break;
201 case kMsgIsServiceRunning:
203 const char* name = message->GetString("name");
204 if (name == NULL)
205 break;
207 BMessage reply(B_REPLY);
208 reply.AddString("name", name);
209 reply.AddBool("running", fNameMap.find(name) != fNameMap.end());
210 message->SendReply(&reply);
211 break;
214 default:
215 BHandler::MessageReceived(message);
220 void
221 Services::_NotifyListener(bool quit)
223 write(fWritePipe, quit ? "q" : "u", 1);
227 void
228 Services::_UpdateMinMaxSocket(int socket)
230 if (socket >= fMaxSocket)
231 fMaxSocket = socket + 1;
232 if (socket < fMinSocket)
233 fMinSocket = socket;
237 status_t
238 Services::_StartService(struct service& service)
240 if (service.stand_alone && service.process == -1) {
241 status_t status = _LaunchService(service, -1);
242 if (status == B_OK) {
243 // add service
244 fNameMap[service.name] = &service;
245 service.update = fUpdate;
247 return status;
250 // create socket
252 bool failed = false;
253 ConnectionList::iterator iterator = service.connections.begin();
254 for (; iterator != service.connections.end(); iterator++) {
255 service_connection& connection = *iterator;
257 connection.socket = socket(connection.Family(),
258 connection.Type(), connection.Protocol());
259 if (connection.socket < 0
260 || bind(connection.socket, connection.Address(),
261 connection.Address().Length()) < 0
262 || fcntl(connection.socket, F_SETFD, FD_CLOEXEC) < 0) {
263 failed = true;
264 break;
267 if (connection.Type() == SOCK_STREAM
268 && listen(connection.socket, 50) < 0) {
269 failed = true;
270 break;
274 if (failed) {
275 // open sockets will be closed when the service is deleted
276 return errno;
279 // add service to maps and activate it
281 fNameMap[service.name] = &service;
282 service.update = fUpdate;
284 iterator = service.connections.begin();
285 for (; iterator != service.connections.end(); iterator++) {
286 service_connection& connection = *iterator;
288 fSocketMap[connection.socket] = &connection;
289 _UpdateMinMaxSocket(connection.socket);
290 FD_SET(connection.socket, &fSet);
293 _NotifyListener();
294 printf("Starting service '%s'\n", service.name.c_str());
295 return B_OK;
299 status_t
300 Services::_StopService(struct service* service)
302 printf("Stop service '%s'\n", service->name.c_str());
304 // remove service from maps
306 ServiceNameMap::iterator iterator = fNameMap.find(service->name);
307 if (iterator != fNameMap.end())
308 fNameMap.erase(iterator);
311 if (!service->stand_alone) {
312 ConnectionList::const_iterator iterator = service->connections.begin();
313 for (; iterator != service->connections.end(); iterator++) {
314 const service_connection& connection = *iterator;
316 ServiceSocketMap::iterator socketIterator
317 = fSocketMap.find(connection.socket);
318 if (socketIterator != fSocketMap.end())
319 fSocketMap.erase(socketIterator);
321 close(connection.socket);
322 FD_CLR(connection.socket, &fSet);
326 // Shutdown the running server, if any
327 if (service->process != -1) {
328 printf(" Sending SIGTERM to process %" B_PRId32 "\n", service->process);
329 kill(-service->process, SIGTERM);
332 delete service;
333 return B_OK;
337 status_t
338 Services::_ToService(const BMessage& message, struct service*& service)
340 BNetworkServiceSettings settings(message);
341 status_t status = settings.InitCheck();
342 if (status != B_OK)
343 return status;
344 if (!settings.IsEnabled())
345 return B_NAME_NOT_FOUND;
347 service = new (std::nothrow) ::service;
348 if (service == NULL)
349 return B_NO_MEMORY;
351 service->name = settings.Name();
352 service->stand_alone = settings.IsStandAlone();
353 service->process = -1;
355 // Copy launch arguments
356 for (int32 i = 0; i < settings.CountArguments(); i++)
357 service->arguments.push_back(settings.ArgumentAt(i));
359 // Copy addresses to listen to
360 for (int32 i = 0; i < settings.CountAddresses(); i++) {
361 const BNetworkServiceAddressSettings& address = settings.AddressAt(i);
362 service_connection connection;
363 connection.owner = service;
364 connection.socket = -1;
365 connection.address = address;
367 service->connections.push_back(connection);
370 return B_OK;
374 void
375 Services::_Update(const BMessage& services)
377 BAutolock locker(fLock);
378 fUpdate++;
380 BMessage message;
381 for (int32 index = 0; services.FindMessage("service", index,
382 &message) == B_OK; index++) {
383 struct service* service;
384 if (_ToService(message, service) != B_OK)
385 continue;
387 ServiceNameMap::iterator iterator = fNameMap.find(service->name);
388 if (iterator == fNameMap.end()) {
389 // this service does not exist yet, start it
390 printf("New service %s\n", service->name.c_str());
391 _StartService(*service);
392 } else {
393 // this service does already exist - check for any changes
395 if (*service != *iterator->second) {
396 printf("Restart service %s\n", service->name.c_str());
397 _StopService(iterator->second);
398 _StartService(*service);
399 } else
400 iterator->second->update = fUpdate;
404 // stop all services that are not part of the update message
406 ServiceNameMap::iterator iterator = fNameMap.begin();
407 while (iterator != fNameMap.end()) {
408 struct service* service = iterator->second;
409 iterator++;
411 if (service->update != fUpdate) {
412 // this service has to be removed
413 _StopService(service);
419 status_t
420 Services::_LaunchService(struct service& service, int socket)
422 printf("Launch service: %s\n", service.arguments[0].c_str());
424 if (socket != -1 && fcntl(socket, F_SETFD, 0) < 0) {
425 // could not clear FD_CLOEXEC on socket
426 return errno;
429 pid_t child = fork();
430 if (child == 0) {
431 setsid();
432 // make sure we're in our own session, and don't accidently quit
433 // the net_server
435 if (socket != -1) {
436 // We're the child, replace standard input/output
437 dup2(socket, STDIN_FILENO);
438 dup2(socket, STDOUT_FILENO);
439 dup2(socket, STDERR_FILENO);
440 close(socket);
443 // build argument array
445 const char** args = (const char**)malloc(
446 (service.arguments.size() + 1) * sizeof(char*));
447 if (args == NULL)
448 exit(1);
450 for (size_t i = 0; i < service.arguments.size(); i++) {
451 args[i] = service.arguments[i].c_str();
453 args[service.arguments.size()] = NULL;
455 if (execv(service.arguments[0].c_str(), (char* const*)args) < 0)
456 exit(1);
458 // we'll never trespass here
459 } else {
460 // the server does not need the socket anymore
461 if (socket != -1)
462 close(socket);
464 if (child < 0) {
465 fprintf(stderr, "Could not start service %s\n",
466 service.name.c_str());
467 } else if (service.stand_alone)
468 service.process = child;
471 // TODO: make sure child started successfully...
472 return B_OK;
476 status_t
477 Services::_Listener()
479 while (true) {
480 fLock.Lock();
481 fd_set set = fSet;
482 fLock.Unlock();
484 if (select(fMaxSocket, &set, NULL, NULL, NULL) < 0) {
485 // sleep a bit before trying again
486 snooze(1000000LL);
489 if (FD_ISSET(fReadPipe, &set)) {
490 char command;
491 if (read(fReadPipe, &command, 1) == 1 && command == 'q')
492 break;
495 BAutolock locker(fLock);
497 for (int i = fMinSocket; i < fMaxSocket; i++) {
498 if (!FD_ISSET(i, &set))
499 continue;
501 ServiceSocketMap::iterator iterator = fSocketMap.find(i);
502 if (iterator == fSocketMap.end())
503 continue;
505 struct service_connection& connection = *iterator->second;
506 int socket;
508 if (connection.Type() == SOCK_STREAM) {
509 // accept incoming connection
510 int value = 1;
511 ioctl(i, FIONBIO, &value);
512 // make sure we don't wait for the connection
514 socket = accept(connection.socket, NULL, NULL);
516 value = 0;
517 ioctl(i, FIONBIO, &value);
519 if (socket < 0)
520 continue;
521 } else
522 socket = connection.socket;
524 // launch this service's handler
526 _LaunchService(*connection.owner, socket);
529 return B_OK;
533 /*static*/ status_t
534 Services::_Listener(void* _self)
536 Services* self = (Services*)_self;
537 return self->_Listener();