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