* Copyright 2001-2005, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* DarkWyrm <bpmagic@columbus.rr.com>
* Adrian Oanca <adioanca@cotty.iren.ro>
* Stephan AΓmus <superstippi@gmx.de>
* Stefano Ceccherini (burton666@libero.it)
* Axel DΓΆrfler, axeld@pinc-software.de
*/
#include <AppDefs.h>
#include <List.h>
#include <String.h>
#include <stdio.h>
#include <string.h>
#include <ServerProtocol.h>
#include "AppServer.h"
#include "ServerApp.h"
#ifdef DEBUG_SERVERAPP
# include <stdio.h>
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
#ifdef DEBUG_SERVERAPP_FONT
# include <stdio.h>
# define FTRACE(x) printf x
#else
# define FTRACE(x) ;
#endif
\brief Constructor
\param sendport port ID for the BApplication which will receive the ServerApp's messages
\param rcvport port by which the ServerApp will receive messages from its BApplication.
\param fSignature NULL-terminated string which contains the BApplication's
MIME fSignature.
*/
ServerApp::ServerApp(port_id sendport, port_id rcvport, port_id clientLooperPort,
team_id clientTeamID, int32 handlerID, const char* signature)
:
fClientAppPort(sendport),
fMessagePort(rcvport),
fClientLooperPort(clientLooperPort),
fSignature(signature),
fMonitorThreadID(-1),
fClientTeamID(clientTeamID),
fLink(fClientAppPort, fMessagePort),
fLockSem(create_sem(1, "ServerApp sem")),
fCursorHidden(false),
fIsActive(false),
fQuitting(false)
{
if (fSignature == "")
fSignature = "application/x-vnd.NULL-application-signature";
Run();
STRACE(("ServerApp %s:\n", fSignature.String()));
STRACE(("\tBApp port: %ld\n", fClientAppPort));
STRACE(("\tReceiver port: %ld\n", fMessagePort));
}
ServerApp::~ServerApp(void)
{
STRACE(("*ServerApp %s:~ServerApp()\n",fSignature.String()));
fQuitting = true;
delete_sem(fLockSem);
STRACE(("#ServerApp %s:~ServerApp()\n", fSignature.String()));
delete_port(fMessagePort);
status_t dummyStatus;
wait_for_thread(fMonitorThreadID, &dummyStatus);
STRACE(("ServerApp %s::~ServerApp(): Exiting\n", fSignature.String()));
}
\brief Starts the ServerApp monitoring for messages
\return false if the application couldn't start, true if everything went OK.
*/
bool
ServerApp::Run(void)
{
fMonitorThreadID = spawn_thread(MonitorApp, fSignature.String(), B_NORMAL_PRIORITY, this);
if (fMonitorThreadID < B_OK)
return false;
return resume_thread(fMonitorThreadID) == B_OK;
}
\brief Pings the target app to make sure it's still working
\return true if target is still "alive" and false if "He's dead, Jim."
"But that's impossible..."
This function is called by the app_server thread to ensure that
the target app still exists. We do this not by sending a message
but by calling get_port_info. We don't want to send ping messages
just because the app might simply be hung. If this is the case, it
should be up to the user to kill it. If the app has been killed, its
ports will be invalid. Thus, if get_port_info returns an error, we
tell the app_server to delete the respective ServerApp.
*/
bool
ServerApp::PingTarget(void)
{
team_info tinfo;
if (get_team_info(fClientTeamID, &tinfo) == B_BAD_TEAM_ID) {
BPrivate::LinkSender link(gAppServerPort);
link.StartMessage(AS_DELETE_APP);
link.Attach(&fMonitorThreadID, sizeof(thread_id));
link.Flush();
return false;
}
return true;
}
\brief Send a message to the ServerApp with no attachments
\param code ID code of the message to post
*/
void
ServerApp::PostMessage(int32 code)
{
BPrivate::LinkSender link(fMessagePort);
link.StartMessage(code);
link.Flush();
}
\brief Send a message to the ServerApp's BApplication
\param msg The message to send
*/
void
ServerApp::SendMessageToClient(const BMessage *msg) const
{
ssize_t size = msg->FlattenedSize();
char *buffer = new char[size];
if (msg->Flatten(buffer, size) == B_OK)
write_port(fClientLooperPort, msg->what, buffer, size);
else
printf("PANIC: ServerApp: '%s': can't flatten message in 'SendMessageToClient()'\n", fSignature.String());
delete [] buffer;
}
\brief Sets the ServerApp's active status
\param value The new status of the ServerApp.
This changes an internal flag and also sets the current cursor to the one specified by
the application
*/
void
ServerApp::Activate(bool value)
{
fIsActive = value;
}
\brief The thread function ServerApps use to monitor messages
\param data Pointer to the thread's ServerApp object
\return Throwaway value - always 0
*/
int32
ServerApp::MonitorApp(void *data)
{
ServerApp *app = (ServerApp *)data;
BPrivate::LinkReceiver &reader = app->fLink.Receiver();
int32 code;
status_t err = B_OK;
while (!app->fQuitting) {
STRACE(("info: ServerApp::MonitorApp listening on port %ld.\n", app->fMessagePort));
err = reader.GetNextMessage(code, B_INFINITE_TIMEOUT);
if (err < B_OK) {
STRACE(("ServerApp::MonitorApp(): GetNextMessage returned %s\n", strerror(err)));
BPrivate::LinkSender link(gAppServerPort);
link.StartMessage(AS_DELETE_APP);
link.Attach(&app->fMonitorThreadID, sizeof(thread_id));
link.Flush();
break;
}
switch (code) {
case AS_QUIT_APP:
{
STRACE(("ServerApp %s:Server shutdown notification received\n",
app->fSignature.String()));
break;
}
case B_QUIT_REQUESTED:
{
STRACE(("ServerApp %s: B_QUIT_REQUESTED\n",app->fSignature.String()));
BPrivate::LinkSender sender(gAppServerPort);
sender.StartMessage(AS_DELETE_APP);
sender.Attach(&app->fMonitorThreadID, sizeof(thread_id));
sender.Flush();
break;
}
default:
STRACE(("ServerApp %s: Got a Message to dispatch\n", app->fSignature.String()));
app->DispatchMessage(code, reader);
break;
}
}
return 0;
}
\brief Handler function for BApplication API messages
\param code Identifier code for the message. Equivalent to BMessage::what
\param buffer Any attachments
Note that the buffer's exact format is determined by the particular message.
All attachments are placed in the buffer via a PortLink, so it will be a
matter of casting and incrementing an index variable to access them.
*/
void
ServerApp::DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
{
switch (code) {
case AS_CURRENT_WORKSPACE:
{
STRACE(("ServerApp %s: get current workspace\n", fSignature.String()));
fLink.StartMessage(B_OK);
fLink.Attach<int32>(0);
fLink.Flush();
break;
}
case AS_ACTIVATE_WORKSPACE:
{
STRACE(("ServerApp %s: activate workspace\n", fSignature.String()));
int32 index;
link.Read<int32>(&index);
break;
}
case AS_QUERY_CURSOR_HIDDEN:
{
STRACE(("ServerApp %s: Received IsCursorHidden request\n", fSignature.String()));
fLink.StartMessage(fCursorHidden ? B_OK : B_ERROR);
fLink.Flush();
break;
}
default:
printf("ServerApp %s received unhandled message code offset %lx\n",
fSignature.String(), code);
if (link.NeedsReply()) {
fLink.StartMessage(B_ERROR);
fLink.Flush();
} else
puts("message doesn't need a reply!");
break;
}
}
team_id
ServerApp::ClientTeamID() const
{
return fClientTeamID;
}
thread_id
ServerApp::MonitorThreadID() const
{
return fMonitorThreadID;
}