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 */
ServerApp(port_id sendport,port_id rcvport,port_id clientLooperPort,team_id clientTeamID,int32 handlerID,const char * signature)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
~ServerApp(void)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
Run(void)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
PingTarget(void)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
PostMessage(int32 code)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
SendMessageToClient(const BMessage * msg) const164 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
Activate(bool value)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
MonitorApp(void * data)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
DispatchMessage(int32 code,BPrivate::LinkReceiver & link)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
ClientTeamID() const323 ServerApp::ClientTeamID() const
324 {
325 return fClientTeamID;
326 }
327
328
329 thread_id
MonitorThreadID() const330 ServerApp::MonitorThreadID() const
331 {
332 return fMonitorThreadID;
333 }
334
335