1/*
2 * Copyright 2001-2005, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		Adrian Oanca <adioanca@cotty.iren.ro>
8 *		Stephan Aßmus <superstippi@gmx.de>
9 *		Stefano Ceccherini (burton666@libero.it)
10 *		Axel Dörfler, axeld@pinc-software.de
11 */
12
13
14#include <AppDefs.h>
15#include <List.h>
16#include <String.h>
17#include <stdio.h>
18#include <string.h>
19#include <ServerProtocol.h>
20
21#include "AppServer.h"
22
23#include "ServerApp.h"
24
25//#define DEBUG_SERVERAPP
26
27#ifdef DEBUG_SERVERAPP
28#	include <stdio.h>
29#	define STRACE(x) printf x
30#else
31#	define STRACE(x) ;
32#endif
33
34//#define DEBUG_SERVERAPP_FONT
35
36#ifdef DEBUG_SERVERAPP_FONT
37#	include <stdio.h>
38#	define FTRACE(x) printf x
39#else
40#	define FTRACE(x) ;
41#endif
42
43/*!
44	\brief Constructor
45	\param sendport port ID for the BApplication which will receive the ServerApp's messages
46	\param rcvport port by which the ServerApp will receive messages from its BApplication.
47	\param fSignature NULL-terminated string which contains the BApplication's
48	MIME fSignature.
49*/
50ServerApp::ServerApp(port_id sendport, port_id rcvport, port_id clientLooperPort,
51	team_id clientTeamID, int32 handlerID, const char* signature)
52	:
53	fClientAppPort(sendport),
54	fMessagePort(rcvport),
55	fClientLooperPort(clientLooperPort),
56	fSignature(signature),
57	fMonitorThreadID(-1),
58	fClientTeamID(clientTeamID),
59	fLink(fClientAppPort, fMessagePort),
60	fLockSem(create_sem(1, "ServerApp sem")),
61	fCursorHidden(false),
62	fIsActive(false),
63	fQuitting(false)
64{
65	if (fSignature == "")
66		fSignature = "application/x-vnd.NULL-application-signature";
67
68	Run();
69
70	STRACE(("ServerApp %s:\n", fSignature.String()));
71	STRACE(("\tBApp port: %ld\n", fClientAppPort));
72	STRACE(("\tReceiver port: %ld\n", fMessagePort));
73}
74
75
76//! Does all necessary teardown for application
77ServerApp::~ServerApp(void)
78{
79	STRACE(("*ServerApp %s:~ServerApp()\n",fSignature.String()));
80
81	fQuitting = true;
82
83	// This shouldn't be necessary -- all cursors owned by the app
84	// should be cleaned up by RemoveAppCursors
85//	if(fAppCursor)
86//		delete fAppCursor;
87
88	delete_sem(fLockSem);
89
90	STRACE(("#ServerApp %s:~ServerApp()\n", fSignature.String()));
91
92	// TODO: Is this the right place for this ?
93	// From what I've understood, this is the port created by
94	// the BApplication (?), but if I delete it in there, GetNextMessage()
95	// in the MonitorApp thread never returns. Cleanup.
96	delete_port(fMessagePort);
97
98	status_t dummyStatus;
99	wait_for_thread(fMonitorThreadID, &dummyStatus);
100
101	STRACE(("ServerApp %s::~ServerApp(): Exiting\n", fSignature.String()));
102}
103
104/*!
105	\brief Starts the ServerApp monitoring for messages
106	\return false if the application couldn't start, true if everything went OK.
107*/
108bool
109ServerApp::Run(void)
110{
111	// Unlike a BApplication, a ServerApp is *supposed* to return immediately
112	// when its Run() function is called.
113	fMonitorThreadID = spawn_thread(MonitorApp, fSignature.String(), B_NORMAL_PRIORITY, this);
114	if (fMonitorThreadID < B_OK)
115		return false;
116
117	return resume_thread(fMonitorThreadID) == B_OK;
118}
119
120/*!
121	\brief Pings the target app to make sure it's still working
122	\return true if target is still "alive" and false if "He's dead, Jim."
123	"But that's impossible..."
124
125	This function is called by the app_server thread to ensure that
126	the target app still exists. We do this not by sending a message
127	but by calling get_port_info. We don't want to send ping messages
128	just because the app might simply be hung. If this is the case, it
129	should be up to the user to kill it. If the app has been killed, its
130	ports will be invalid. Thus, if get_port_info returns an error, we
131	tell the app_server to delete the respective ServerApp.
132*/
133bool
134ServerApp::PingTarget(void)
135{
136	team_info tinfo;
137	if (get_team_info(fClientTeamID, &tinfo) == B_BAD_TEAM_ID) {
138		BPrivate::LinkSender link(gAppServerPort);
139		link.StartMessage(AS_DELETE_APP);
140		link.Attach(&fMonitorThreadID, sizeof(thread_id));
141		link.Flush();
142		return false;
143	}
144	return true;
145}
146
147/*!
148	\brief Send a message to the ServerApp with no attachments
149	\param code ID code of the message to post
150*/
151void
152ServerApp::PostMessage(int32 code)
153{
154	BPrivate::LinkSender link(fMessagePort);
155	link.StartMessage(code);
156	link.Flush();
157}
158
159/*!
160	\brief Send a message to the ServerApp's BApplication
161	\param msg The message to send
162*/
163void
164ServerApp::SendMessageToClient(const BMessage *msg) const
165{
166	ssize_t size = msg->FlattenedSize();
167	char *buffer = new char[size];
168
169	if (msg->Flatten(buffer, size) == B_OK)
170		write_port(fClientLooperPort, msg->what, buffer, size);
171	else
172		printf("PANIC: ServerApp: '%s': can't flatten message in 'SendMessageToClient()'\n", fSignature.String());
173
174	delete [] buffer;
175}
176
177/*!
178	\brief Sets the ServerApp's active status
179	\param value The new status of the ServerApp.
180
181	This changes an internal flag and also sets the current cursor to the one specified by
182	the application
183*/
184void
185ServerApp::Activate(bool value)
186{
187	fIsActive = value;
188}
189
190
191/*!
192	\brief The thread function ServerApps use to monitor messages
193	\param data Pointer to the thread's ServerApp object
194	\return Throwaway value - always 0
195*/
196int32
197ServerApp::MonitorApp(void *data)
198{
199	// Message-dispatching loop for the ServerApp
200
201	ServerApp *app = (ServerApp *)data;
202	BPrivate::LinkReceiver &reader = app->fLink.Receiver();
203
204	int32 code;
205	status_t err = B_OK;
206
207	while (!app->fQuitting) {
208		STRACE(("info: ServerApp::MonitorApp listening on port %ld.\n", app->fMessagePort));
209		err = reader.GetNextMessage(code, B_INFINITE_TIMEOUT);
210		if (err < B_OK) {
211			STRACE(("ServerApp::MonitorApp(): GetNextMessage returned %s\n", strerror(err)));
212
213			// ToDo: this should kill the app, but it doesn't work
214			BPrivate::LinkSender link(gAppServerPort);
215			link.StartMessage(AS_DELETE_APP);
216			link.Attach(&app->fMonitorThreadID, sizeof(thread_id));
217			link.Flush();
218			break;
219		}
220
221		switch (code) {
222			case AS_QUIT_APP:
223			{
224				// This message is received only when the app_server is asked to shut down in
225				// test/debug mode. Of course, if we are testing while using AccelerantDriver, we do
226				// NOT want to shut down client applications. The server can be quit o in this fashion
227				// through the driver's interface, such as closing the ViewDriver's window.
228
229				STRACE(("ServerApp %s:Server shutdown notification received\n",
230					app->fSignature.String()));
231
232				// If we are using the real, accelerated version of the
233				// DisplayDriver, we do NOT want the user to be able shut down
234				// the server. The results would NOT be pretty
235				break;
236			}
237
238			case B_QUIT_REQUESTED:
239			{
240				STRACE(("ServerApp %s: B_QUIT_REQUESTED\n",app->fSignature.String()));
241				// Our BApplication sent us this message when it quit.
242				// We need to ask the app_server to delete ourself.
243				BPrivate::LinkSender sender(gAppServerPort);
244				sender.StartMessage(AS_DELETE_APP);
245				sender.Attach(&app->fMonitorThreadID, sizeof(thread_id));
246				sender.Flush();
247				break;
248			}
249
250			default:
251				STRACE(("ServerApp %s: Got a Message to dispatch\n", app->fSignature.String()));
252				app->DispatchMessage(code, reader);
253				break;
254		}
255	}
256
257	return 0;
258}
259
260
261/*!
262	\brief Handler function for BApplication API messages
263	\param code Identifier code for the message. Equivalent to BMessage::what
264	\param buffer Any attachments
265
266	Note that the buffer's exact format is determined by the particular message.
267	All attachments are placed in the buffer via a PortLink, so it will be a
268	matter of casting and incrementing an index variable to access them.
269*/
270void
271ServerApp::DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
272{
273	switch (code) {
274		case AS_CURRENT_WORKSPACE:
275		{
276			STRACE(("ServerApp %s: get current workspace\n", fSignature.String()));
277
278			// TODO: Locking this way is not nice
279			fLink.StartMessage(B_OK);
280			fLink.Attach<int32>(0);
281			fLink.Flush();
282			break;
283		}
284
285		case AS_ACTIVATE_WORKSPACE:
286		{
287			STRACE(("ServerApp %s: activate workspace\n", fSignature.String()));
288
289			// TODO: See above
290			int32 index;
291			link.Read<int32>(&index);
292			// no reply
293
294			break;
295		}
296
297		case AS_QUERY_CURSOR_HIDDEN:
298		{
299			STRACE(("ServerApp %s: Received IsCursorHidden request\n", fSignature.String()));
300			// Attached data
301			// 1) int32 port to reply to
302			fLink.StartMessage(fCursorHidden ? B_OK : B_ERROR);
303			fLink.Flush();
304			break;
305		}
306
307		default:
308			printf("ServerApp %s received unhandled message code offset %lx\n",
309				fSignature.String(), code);
310
311			if (link.NeedsReply()) {
312				// the client is now blocking and waiting for a reply!
313				fLink.StartMessage(B_ERROR);
314				fLink.Flush();
315			} else
316				puts("message doesn't need a reply!");
317			break;
318	}
319}
320
321
322team_id
323ServerApp::ClientTeamID() const
324{
325	return fClientTeamID;
326}
327
328
329thread_id
330ServerApp::MonitorThreadID() const
331{
332	return fMonitorThreadID;
333}
334
335