2 * Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
15 #include <netinet/in.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
22 #include <NetworkAddress.h>
23 #include <NetworkSettings.h>
25 #include <NetServer.h>
29 using namespace BNetworkKit
;
32 struct service_connection
{
33 struct service
* owner
;
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
;
55 ConnectionList connections
;
61 bool operator!=(const struct service
& other
) const;
62 bool operator==(const struct service
& other
) const;
70 service_connection::operator==(const struct service_connection
& other
) const
72 return address
== other
.address
;
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
);
92 service::operator!=(const struct service
& other
) const
94 return !(*this == other
);
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
)
109 for(size_t i
= 0; i
< arguments
.size(); i
++) {
110 if (arguments
[i
] != other
.arguments
[i
])
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
)
129 if (otherIterator
== other
.connections
.end())
140 Services::Services(const BMessage
& services
)
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) {
153 fcntl(fReadPipe
, F_SETFD
, FD_CLOEXEC
);
154 fcntl(fWritePipe
, F_SETFD
, FD_CLOEXEC
);
157 FD_SET(fReadPipe
, &fSet
);
159 fMinSocket
= fWritePipe
+ 1;
160 fMaxSocket
= fWritePipe
+ 1;
164 fListener
= spawn_thread(_Listener
, "services listener", B_NORMAL_PRIORITY
,
166 if (fListener
>= B_OK
)
167 resume_thread(fListener
);
171 Services::~Services()
173 wait_for_thread(fListener
, NULL
);
180 while (!fNameMap
.empty()) {
181 _StopService(fNameMap
.begin()->second
);
187 Services::InitCheck() const
189 return fListener
>= B_OK
? B_OK
: fListener
;
194 Services::MessageReceived(BMessage
* message
)
196 switch (message
->what
) {
197 case kMsgUpdateServices
:
201 case kMsgIsServiceRunning
:
203 const char* name
= message
->GetString("name");
207 BMessage
reply(B_REPLY
);
208 reply
.AddString("name", name
);
209 reply
.AddBool("running", fNameMap
.find(name
) != fNameMap
.end());
210 message
->SendReply(&reply
);
215 BHandler::MessageReceived(message
);
221 Services::_NotifyListener(bool quit
)
223 write(fWritePipe
, quit
? "q" : "u", 1);
228 Services::_UpdateMinMaxSocket(int socket
)
230 if (socket
>= fMaxSocket
)
231 fMaxSocket
= socket
+ 1;
232 if (socket
< fMinSocket
)
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
) {
244 fNameMap
[service
.name
] = &service
;
245 service
.update
= fUpdate
;
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) {
267 if (connection
.Type() == SOCK_STREAM
268 && listen(connection
.socket
, 50) < 0) {
275 // open sockets will be closed when the service is deleted
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
);
294 printf("Starting service '%s'\n", service
.name
.c_str());
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
);
338 Services::_ToService(const BMessage
& message
, struct service
*& service
)
340 BNetworkServiceSettings
settings(message
);
341 status_t status
= settings
.InitCheck();
344 if (!settings
.IsEnabled())
345 return B_NAME_NOT_FOUND
;
347 service
= new (std::nothrow
) ::service
;
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
);
375 Services::_Update(const BMessage
& services
)
377 BAutolock
locker(fLock
);
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
)
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
);
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
);
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
;
411 if (service
->update
!= fUpdate
) {
412 // this service has to be removed
413 _StopService(service
);
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
429 pid_t child
= fork();
432 // make sure we're in our own session, and don't accidently quit
436 // We're the child, replace standard input/output
437 dup2(socket
, STDIN_FILENO
);
438 dup2(socket
, STDOUT_FILENO
);
439 dup2(socket
, STDERR_FILENO
);
443 // build argument array
445 const char** args
= (const char**)malloc(
446 (service
.arguments
.size() + 1) * sizeof(char*));
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)
458 // we'll never trespass here
460 // the server does not need the socket anymore
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...
477 Services::_Listener()
484 if (select(fMaxSocket
, &set
, NULL
, NULL
, NULL
) < 0) {
485 // sleep a bit before trying again
489 if (FD_ISSET(fReadPipe
, &set
)) {
491 if (read(fReadPipe
, &command
, 1) == 1 && command
== 'q')
495 BAutolock
locker(fLock
);
497 for (int i
= fMinSocket
; i
< fMaxSocket
; i
++) {
498 if (!FD_ISSET(i
, &set
))
501 ServiceSocketMap::iterator iterator
= fSocketMap
.find(i
);
502 if (iterator
== fSocketMap
.end())
505 struct service_connection
& connection
= *iterator
->second
;
508 if (connection
.Type() == SOCK_STREAM
) {
509 // accept incoming connection
511 ioctl(i
, FIONBIO
, &value
);
512 // make sure we don't wait for the connection
514 socket
= accept(connection
.socket
, NULL
, NULL
);
517 ioctl(i
, FIONBIO
, &value
);
522 socket
= connection
.socket
;
524 // launch this service's handler
526 _LaunchService(*connection
.owner
, socket
);
534 Services::_Listener(void* _self
)
536 Services
* self
= (Services
*)_self
;
537 return self
->_Listener();