1/*
2 * Copyright 2006-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 */
8
9
10#include "Services.h"
11
12#include <new>
13
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>
20
21#include <Autolock.h>
22#include <NetworkAddress.h>
23#include <NetworkSettings.h>
24
25#include <NetServer.h>
26
27
28using namespace std;
29using namespace BNetworkKit;
30
31
32struct service_connection {
33	struct service* owner;
34	int		socket;
35	BNetworkServiceAddressSettings address;
36
37	//service_connection() : owner(NULL), socket(-1) {}
38
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(); }
43
44	bool operator==(const struct service_connection& other) const;
45};
46
47typedef std::vector<service_connection> ConnectionList;
48typedef std::vector<std::string> StringList;
49
50struct 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;
59
60	~service();
61	bool operator!=(const struct service& other) const;
62	bool operator==(const struct service& other) const;
63};
64
65
66//	#pragma mark -
67
68
69bool
70service_connection::operator==(const struct service_connection& other) const
71{
72	return address == other.address;
73}
74
75
76//	#pragma mark -
77
78
79service::~service()
80{
81	// close all open sockets
82	ConnectionList::const_iterator iterator = connections.begin();
83	for (; iterator != connections.end(); iterator++) {
84		const service_connection& connection = *iterator;
85
86		close(connection.socket);
87	}
88}
89
90
91bool
92service::operator!=(const struct service& other) const
93{
94	return !(*this == other);
95}
96
97
98bool
99service::operator==(const struct service& other) const
100{
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;
106
107	// Compare arguments
108
109	for(size_t i = 0; i < arguments.size(); i++) {
110		if (arguments[i] != other.arguments[i])
111			return false;
112	}
113
114	// Compare connections
115
116	ConnectionList::const_iterator iterator = connections.begin();
117	for (; iterator != connections.end(); iterator++) {
118		const service_connection& connection = *iterator;
119
120		// Find address in other addresses
121
122		ConnectionList::const_iterator otherIterator
123			= other.connections.begin();
124		for (; otherIterator != other.connections.end(); otherIterator++) {
125			if (connection == *otherIterator)
126				break;
127		}
128
129		if (otherIterator == other.connections.end())
130			return false;
131	}
132
133	return true;
134}
135
136
137//	#pragma mark -
138
139
140Services::Services(const BMessage& services)
141	:
142	fListener(-1),
143	fUpdate(0),
144	fMaxSocket(0)
145{
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;
151	}
152
153	fcntl(fReadPipe, F_SETFD, FD_CLOEXEC);
154	fcntl(fWritePipe, F_SETFD, FD_CLOEXEC);
155
156	FD_ZERO(&fSet);
157	FD_SET(fReadPipe, &fSet);
158
159	fMinSocket = fWritePipe + 1;
160	fMaxSocket = fWritePipe + 1;
161
162	_Update(services);
163
164	fListener = spawn_thread(_Listener, "services listener", B_NORMAL_PRIORITY,
165		this);
166	if (fListener >= B_OK)
167		resume_thread(fListener);
168}
169
170
171Services::~Services()
172{
173	wait_for_thread(fListener, NULL);
174
175	close(fReadPipe);
176	close(fWritePipe);
177
178	// stop all services
179
180	while (!fNameMap.empty()) {
181		_StopService(fNameMap.begin()->second);
182	}
183}
184
185
186status_t
187Services::InitCheck() const
188{
189	return fListener >= B_OK ? B_OK : fListener;
190}
191
192
193void
194Services::MessageReceived(BMessage* message)
195{
196	switch (message->what) {
197		case kMsgUpdateServices:
198			_Update(*message);
199			break;
200
201		case kMsgIsServiceRunning:
202		{
203			const char* name = message->GetString("name");
204			if (name == NULL)
205				break;
206
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;
212		}
213
214		default:
215			BHandler::MessageReceived(message);
216	}
217}
218
219
220void
221Services::_NotifyListener(bool quit)
222{
223	write(fWritePipe, quit ? "q" : "u", 1);
224}
225
226
227void
228Services::_UpdateMinMaxSocket(int socket)
229{
230	if (socket >= fMaxSocket)
231		fMaxSocket = socket + 1;
232	if (socket < fMinSocket)
233		fMinSocket = socket;
234}
235
236
237status_t
238Services::_StartService(struct service& service)
239{
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;
246		}
247		return status;
248	}
249
250	// create socket
251
252	bool failed = false;
253	ConnectionList::iterator iterator = service.connections.begin();
254	for (; iterator != service.connections.end(); iterator++) {
255		service_connection& connection = *iterator;
256
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;
265		}
266
267		if (connection.Type() == SOCK_STREAM
268			&& listen(connection.socket, 50) < 0) {
269			failed = true;
270			break;
271		}
272	}
273
274	if (failed) {
275		// open sockets will be closed when the service is deleted
276		return errno;
277	}
278
279	// add service to maps and activate it
280
281	fNameMap[service.name] = &service;
282	service.update = fUpdate;
283
284	iterator = service.connections.begin();
285	for (; iterator != service.connections.end(); iterator++) {
286		service_connection& connection = *iterator;
287
288		fSocketMap[connection.socket] = &connection;
289		_UpdateMinMaxSocket(connection.socket);
290		FD_SET(connection.socket, &fSet);
291	}
292
293	_NotifyListener();
294	printf("Starting service '%s'\n", service.name.c_str());
295	return B_OK;
296}
297
298
299status_t
300Services::_StopService(struct service* service)
301{
302	printf("Stop service '%s'\n", service->name.c_str());
303
304	// remove service from maps
305	{
306		ServiceNameMap::iterator iterator = fNameMap.find(service->name);
307		if (iterator != fNameMap.end())
308			fNameMap.erase(iterator);
309	}
310
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;
315
316			ServiceSocketMap::iterator socketIterator
317				= fSocketMap.find(connection.socket);
318			if (socketIterator != fSocketMap.end())
319				fSocketMap.erase(socketIterator);
320
321			close(connection.socket);
322			FD_CLR(connection.socket, &fSet);
323		}
324	}
325
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);
330	}
331
332	delete service;
333	return B_OK;
334}
335
336
337status_t
338Services::_ToService(const BMessage& message, struct service*& service)
339{
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;
346
347	service = new (std::nothrow) ::service;
348	if (service == NULL)
349		return B_NO_MEMORY;
350
351	service->name = settings.Name();
352	service->stand_alone = settings.IsStandAlone();
353	service->process = -1;
354
355	// Copy launch arguments
356	for (int32 i = 0; i < settings.CountArguments(); i++)
357		service->arguments.push_back(settings.ArgumentAt(i));
358
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;
366
367		service->connections.push_back(connection);
368	}
369
370	return B_OK;
371}
372
373
374void
375Services::_Update(const BMessage& services)
376{
377	BAutolock locker(fLock);
378	fUpdate++;
379
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;
386
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
394
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;
401		}
402	}
403
404	// stop all services that are not part of the update message
405
406	ServiceNameMap::iterator iterator = fNameMap.begin();
407	while (iterator != fNameMap.end()) {
408		struct service* service = iterator->second;
409		iterator++;
410
411		if (service->update != fUpdate) {
412			// this service has to be removed
413			_StopService(service);
414		}
415	}
416}
417
418
419status_t
420Services::_LaunchService(struct service& service, int socket)
421{
422	printf("Launch service: %s\n", service.arguments[0].c_str());
423
424	if (socket != -1 && fcntl(socket, F_SETFD, 0) < 0) {
425		// could not clear FD_CLOEXEC on socket
426		return errno;
427	}
428
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
434
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);
441		}
442
443		// build argument array
444
445		const char** args = (const char**)malloc(
446			(service.arguments.size() + 1) * sizeof(char*));
447		if (args == NULL)
448			exit(1);
449
450		for (size_t i = 0; i < service.arguments.size(); i++) {
451			args[i] = service.arguments[i].c_str();
452		}
453		args[service.arguments.size()] = NULL;
454
455		if (execv(service.arguments[0].c_str(), (char* const*)args) < 0)
456			exit(1);
457
458		// we'll never trespass here
459	} else {
460		// the server does not need the socket anymore
461		if (socket != -1)
462			close(socket);
463
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;
469	}
470
471	// TODO: make sure child started successfully...
472	return B_OK;
473}
474
475
476status_t
477Services::_Listener()
478{
479	while (true) {
480		fLock.Lock();
481		fd_set set = fSet;
482		fLock.Unlock();
483
484		if (select(fMaxSocket, &set, NULL, NULL, NULL) < 0) {
485			// sleep a bit before trying again
486			snooze(1000000LL);
487		}
488
489		if (FD_ISSET(fReadPipe, &set)) {
490			char command;
491			if (read(fReadPipe, &command, 1) == 1 && command == 'q')
492				break;
493		}
494
495		BAutolock locker(fLock);
496
497		for (int i = fMinSocket; i < fMaxSocket; i++) {
498			if (!FD_ISSET(i, &set))
499				continue;
500
501			ServiceSocketMap::iterator iterator = fSocketMap.find(i);
502			if (iterator == fSocketMap.end())
503				continue;
504
505			struct service_connection& connection = *iterator->second;
506			int socket;
507
508			if (connection.Type() == SOCK_STREAM) {
509				// accept incoming connection
510				int value = 1;
511				ioctl(i, FIONBIO, &value, sizeof(value));
512					// make sure we don't wait for the connection
513
514				socket = accept(connection.socket, NULL, NULL);
515
516				value = 0;
517				ioctl(i, FIONBIO, &value, sizeof(value));
518
519				if (socket < 0)
520					continue;
521			} else
522				socket = connection.socket;
523
524			// launch this service's handler
525
526			_LaunchService(*connection.owner, socket);
527		}
528	}
529	return B_OK;
530}
531
532
533/*static*/ status_t
534Services::_Listener(void* _self)
535{
536	Services* self = (Services*)_self;
537	return self->_Listener();
538}
539