xref: /haiku/src/tests/apps/fake_app_server/ServerApp.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
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 */
50 ServerApp::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
77 ServerApp::~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 */
108 bool
109 ServerApp::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 */
133 bool
134 ServerApp::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 */
151 void
152 ServerApp::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 */
163 void
164 ServerApp::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 */
184 void
185 ServerApp::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 */
196 int32
197 ServerApp::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 */
270 void
271 ServerApp::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 
322 team_id
323 ServerApp::ClientTeamID() const
324 {
325 	return fClientTeamID;
326 }
327 
328 
329 thread_id
330 ServerApp::MonitorThreadID() const
331 {
332 	return fMonitorThreadID;
333 }
334 
335