* Moved TRoster out of the BPrivate namespace. It does no longer appear in
any public header. * Replaced a good deal of the MessageDeliverer's DeliverMessage() versions by more general ones using the new interface MessagingTargetSet to represent a set of targets. This simplifies the usage in cases where the caller doesn't already have the targets in a supported representation. * Implemented a first approximation of the shutdown process. There is no GUI yet. Only superficially tested under R5. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13417 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
925d069d23
commit
553ea30124
@ -46,10 +46,6 @@
|
||||
class BHandler;
|
||||
class BLooper;
|
||||
|
||||
namespace BPrivate {
|
||||
class TRoster;
|
||||
};
|
||||
|
||||
// BMessenger class ------------------------------------------------------------
|
||||
class BMessenger {
|
||||
public:
|
||||
|
@ -56,8 +56,7 @@ AppInfoList::AppInfoList()
|
||||
AppInfoList::~AppInfoList()
|
||||
{
|
||||
// delete all infos
|
||||
for (int32 i = 0; RosterAppInfo *info = InfoAt(i); i++)
|
||||
delete info;
|
||||
MakeEmpty(true);
|
||||
}
|
||||
|
||||
// AddInfo
|
||||
@ -90,8 +89,13 @@ AppInfoList::RemoveInfo(RosterAppInfo *info)
|
||||
/*! \brief Removes all RosterAppInfos from the list.
|
||||
*/
|
||||
void
|
||||
AppInfoList::MakeEmpty()
|
||||
AppInfoList::MakeEmpty(bool deleteInfos)
|
||||
{
|
||||
if (deleteInfos) {
|
||||
for (int32 i = 0; RosterAppInfo *info = InfoAt(i); i++)
|
||||
delete info;
|
||||
}
|
||||
|
||||
fInfos.MakeEmpty();
|
||||
}
|
||||
|
||||
|
@ -45,13 +45,14 @@ public:
|
||||
|
||||
bool AddInfo(RosterAppInfo *info);
|
||||
bool RemoveInfo(RosterAppInfo *info);
|
||||
void MakeEmpty();
|
||||
void MakeEmpty(bool deleteInfos = false);
|
||||
|
||||
RosterAppInfo *InfoFor(const char *signature) const;
|
||||
RosterAppInfo *InfoFor(team_id team) const;
|
||||
RosterAppInfo *InfoFor(const entry_ref *ref) const;
|
||||
RosterAppInfo *InfoForToken(uint32 token) const;
|
||||
|
||||
bool IsEmpty() const { return (CountInfos() == 0); };
|
||||
int32 CountInfos() const;
|
||||
|
||||
Iterator It();
|
||||
|
74
src/servers/registrar/AppInfoListMessagingTargetSet.cpp
Normal file
74
src/servers/registrar/AppInfoListMessagingTargetSet.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <Application.h>
|
||||
#include <TokenSpace.h>
|
||||
|
||||
#include "AppInfoListMessagingTargetSet.h"
|
||||
#include "RosterAppInfo.h"
|
||||
|
||||
// constructor
|
||||
AppInfoListMessagingTargetSet::AppInfoListMessagingTargetSet(
|
||||
AppInfoList &list, bool skipRegistrar)
|
||||
: fList(list),
|
||||
fIterator(list.It()),
|
||||
fSkipRegistrar(skipRegistrar)
|
||||
{
|
||||
_SkipFilteredOutInfos();
|
||||
}
|
||||
|
||||
// destructor
|
||||
AppInfoListMessagingTargetSet::~AppInfoListMessagingTargetSet()
|
||||
{
|
||||
}
|
||||
|
||||
// HasNext
|
||||
bool
|
||||
AppInfoListMessagingTargetSet::HasNext() const
|
||||
{
|
||||
return fIterator.IsValid();
|
||||
}
|
||||
|
||||
// Next
|
||||
bool
|
||||
AppInfoListMessagingTargetSet::Next(port_id &port, int32 &token)
|
||||
{
|
||||
if (!fIterator.IsValid())
|
||||
return false;
|
||||
|
||||
port = (*fIterator)->port;
|
||||
token = B_PREFERRED_TOKEN;
|
||||
|
||||
++fIterator;
|
||||
_SkipFilteredOutInfos();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
void
|
||||
AppInfoListMessagingTargetSet::Rewind()
|
||||
{
|
||||
fIterator = fList.It();
|
||||
}
|
||||
|
||||
// Filter
|
||||
bool
|
||||
AppInfoListMessagingTargetSet::Filter(const RosterAppInfo *info)
|
||||
{
|
||||
if (!fSkipRegistrar)
|
||||
return true;
|
||||
|
||||
return (!fSkipRegistrar || info->team != be_app->Team());
|
||||
}
|
||||
|
||||
// _SkipFilteredOutInfos
|
||||
void
|
||||
AppInfoListMessagingTargetSet::_SkipFilteredOutInfos()
|
||||
{
|
||||
while (fIterator.IsValid() && !Filter(*fIterator))
|
||||
++fIterator;
|
||||
}
|
||||
|
37
src/servers/registrar/AppInfoListMessagingTargetSet.h
Normal file
37
src/servers/registrar/AppInfoListMessagingTargetSet.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Implements the MessagingTargetSet interface for AppInfoLists, so that
|
||||
* no other representation (array/list) is needed to feed them into the
|
||||
* MessageDeliverer.
|
||||
*/
|
||||
#ifndef APP_INFO_LIST_MESSAGING_TARGET_SET_H
|
||||
#define APP_INFO_LIST_MESSAGING_TARGET_SET_H
|
||||
|
||||
#include "AppInfoList.h"
|
||||
#include "MessageDeliverer.h"
|
||||
|
||||
class RosterAppInfo;
|
||||
|
||||
class AppInfoListMessagingTargetSet : public MessagingTargetSet {
|
||||
public:
|
||||
AppInfoListMessagingTargetSet(AppInfoList &list,
|
||||
bool skipRegistrar = true);
|
||||
virtual ~AppInfoListMessagingTargetSet();
|
||||
|
||||
virtual bool HasNext() const;
|
||||
virtual bool Next(port_id &port, int32 &token);
|
||||
virtual void Rewind();
|
||||
|
||||
virtual bool Filter(const RosterAppInfo *info);
|
||||
|
||||
private:
|
||||
void _SkipFilteredOutInfos();
|
||||
|
||||
AppInfoList &fList;
|
||||
AppInfoList::Iterator fIterator;
|
||||
bool fSkipRegistrar;
|
||||
};
|
||||
|
||||
#endif // APP_INFO_LIST_MESSAGING_TARGET_SET_H
|
@ -9,6 +9,7 @@ AddResources obos_registrar : registrar.rdef ;
|
||||
|
||||
local registrar_sources =
|
||||
AppInfoList.cpp
|
||||
AppInfoListMessagingTargetSet.cpp
|
||||
Clipboard.cpp
|
||||
ClipboardHandler.cpp
|
||||
Event.cpp
|
||||
@ -27,6 +28,7 @@ local registrar_sources =
|
||||
Registrar.cpp
|
||||
RosterAppInfo.cpp
|
||||
RosterSettingsCharStream.cpp
|
||||
ShutdownProcess.cpp
|
||||
TRoster.cpp
|
||||
Watcher.cpp
|
||||
WatchingService.cpp
|
||||
|
@ -31,6 +31,123 @@ static const bigtime_t kRetryDelay = 100000; // 100 ms
|
||||
static const int32 kMaxMessagesPerPort = 10000;
|
||||
static const int32 kMaxDataPerPort = 50 * 1024 * 1024; // 50 MB
|
||||
|
||||
|
||||
// MessagingTargetSet
|
||||
|
||||
// destructor
|
||||
MessagingTargetSet::~MessagingTargetSet()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// DefaultMessagingTargetSet
|
||||
|
||||
// constructor
|
||||
DefaultMessagingTargetSet::DefaultMessagingTargetSet(
|
||||
const messaging_target *targets, int32 targetCount)
|
||||
: MessagingTargetSet(),
|
||||
fTargets(targets),
|
||||
fTargetCount(targetCount),
|
||||
fNextIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
DefaultMessagingTargetSet::~DefaultMessagingTargetSet()
|
||||
{
|
||||
}
|
||||
|
||||
// HasNext
|
||||
bool
|
||||
DefaultMessagingTargetSet::HasNext() const
|
||||
{
|
||||
return (fNextIndex < fTargetCount);
|
||||
}
|
||||
|
||||
// Next
|
||||
bool
|
||||
DefaultMessagingTargetSet::Next(port_id &port, int32 &token)
|
||||
{
|
||||
if (fNextIndex >= fTargetCount)
|
||||
return false;
|
||||
|
||||
port = fTargets[fNextIndex].port;
|
||||
token = fTargets[fNextIndex].token;
|
||||
fNextIndex++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
void
|
||||
DefaultMessagingTargetSet::Rewind()
|
||||
{
|
||||
fNextIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// SingleMessagingTargetSet
|
||||
|
||||
// constructor
|
||||
SingleMessagingTargetSet::SingleMessagingTargetSet(BMessenger target)
|
||||
: MessagingTargetSet(),
|
||||
fAtBeginning(true)
|
||||
{
|
||||
BMessenger::Private messengerPrivate(target);
|
||||
fPort = messengerPrivate.Port();
|
||||
fToken = (messengerPrivate.IsPreferredTarget()
|
||||
? B_PREFERRED_TOKEN : messengerPrivate.Token());
|
||||
}
|
||||
|
||||
// constructor
|
||||
SingleMessagingTargetSet::SingleMessagingTargetSet(port_id port, int32 token)
|
||||
: MessagingTargetSet(),
|
||||
fPort(port),
|
||||
fToken(token),
|
||||
fAtBeginning(true)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
SingleMessagingTargetSet::~SingleMessagingTargetSet()
|
||||
{
|
||||
}
|
||||
|
||||
// HasNext
|
||||
bool
|
||||
SingleMessagingTargetSet::HasNext() const
|
||||
{
|
||||
return fAtBeginning;
|
||||
}
|
||||
|
||||
// Next
|
||||
bool
|
||||
SingleMessagingTargetSet::Next(port_id &port, int32 &token)
|
||||
{
|
||||
if (!fAtBeginning)
|
||||
return false;
|
||||
|
||||
port = fPort;
|
||||
token = fToken;
|
||||
fAtBeginning = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
void
|
||||
SingleMessagingTargetSet::Rewind()
|
||||
{
|
||||
fAtBeginning = true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// Message
|
||||
/*! \brief Encapsulates a message to be delivered.
|
||||
|
||||
@ -457,109 +574,8 @@ status_t
|
||||
MessageDeliverer::DeliverMessage(BMessage *message, BMessenger target,
|
||||
bigtime_t timeout)
|
||||
{
|
||||
BMessenger::Private messengerPrivate(target);
|
||||
return DeliverMessage(message, messengerPrivate.Port(),
|
||||
messengerPrivate.IsPreferredTarget()
|
||||
? B_PREFERRED_TOKEN : messengerPrivate.Token(),
|
||||
timeout);
|
||||
}
|
||||
|
||||
// DeliverMessage
|
||||
/*! \brief Delivers a message to the supplied target.
|
||||
|
||||
The method tries to send the message right now (if there are not already
|
||||
messages pending for the target port). If that fails due to a full target
|
||||
port, the message is queued for later delivery.
|
||||
|
||||
\param message The message to be delivered.
|
||||
\param port The port the message shall be sent to.
|
||||
\param token The token identifying the target BHandler.
|
||||
\param timeout If given, the message will be dropped, when it couldn't be
|
||||
delivered after this amount of microseconds.
|
||||
\return
|
||||
- \c B_OK, if sending the message succeeded or if the target port was
|
||||
full and the message has been queued,
|
||||
- another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
MessageDeliverer::DeliverMessage(BMessage *message, port_id port, int32 token,
|
||||
bigtime_t timeout)
|
||||
{
|
||||
if (!message)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// Set the token now, so that the header contains room for it.
|
||||
// It will be set when sending the message anyway, but if it is not set
|
||||
// before flattening, the header will not contain room for it, and it
|
||||
// will not possible to send the message flattened later.
|
||||
BMessage::Private(message).SetTarget(token, (token < 0));
|
||||
|
||||
// flatten the message
|
||||
BMallocIO mallocIO;
|
||||
status_t error = message->Flatten(&mallocIO);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return DeliverMessage(mallocIO.Buffer(), mallocIO.BufferLength(), port,
|
||||
token, timeout);
|
||||
}
|
||||
|
||||
// DeliverMessage
|
||||
/*! \brief Delivers a flattened message to the supplied target.
|
||||
|
||||
The method tries to send the message right now (if there are not already
|
||||
messages pending for the target port). If that fails due to a full target
|
||||
port, the message is queued for later delivery.
|
||||
|
||||
\param message The flattened message to be delivered. This may be a
|
||||
flattened BMessage or KMessage.
|
||||
\param messageSize The size of the flattened message buffer.
|
||||
\param target A BMessenger identifying the delivery target.
|
||||
\param timeout If given, the message will be dropped, when it couldn't be
|
||||
delivered after this amount of microseconds.
|
||||
\return
|
||||
- \c B_OK, if sending the message succeeded or if the target port was
|
||||
full and the message has been queued,
|
||||
- another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
MessageDeliverer::DeliverMessage(const void *message, int32 messageSize,
|
||||
BMessenger target, bigtime_t timeout)
|
||||
{
|
||||
BMessenger::Private messengerPrivate(target);
|
||||
return DeliverMessage(message, messageSize, messengerPrivate.Port(),
|
||||
messengerPrivate.IsPreferredTarget()
|
||||
? B_PREFERRED_TOKEN : messengerPrivate.Token(),
|
||||
timeout);
|
||||
}
|
||||
|
||||
// DeliverMessage
|
||||
/*! \brief Delivers a flattened message to the supplied target.
|
||||
|
||||
The method tries to send the message right now (if there are not already
|
||||
messages pending for the target port). If that fails due to a full target
|
||||
port, the message is queued for later delivery.
|
||||
|
||||
\param message The flattened message to be delivered. This may be a
|
||||
flattened BMessage or KMessage.
|
||||
\param messageSize The size of the flattened message buffer.
|
||||
\param port The port the message shall be sent to.
|
||||
\param token The token identifying the target BHandler.
|
||||
\param timeout If given, the message will be dropped, when it couldn't be
|
||||
delivered after this amount of microseconds.
|
||||
\return
|
||||
- \c B_OK, if sending the message succeeded or if the target port was
|
||||
full and the message has been queued,
|
||||
- another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
MessageDeliverer::DeliverMessage(const void *message, int32 messageSize,
|
||||
port_id port, int32 token, bigtime_t timeout)
|
||||
{
|
||||
messaging_target target;
|
||||
target.port = port;
|
||||
target.token = token;
|
||||
return DeliverMessage(message, messageSize, &target, 1, timeout);
|
||||
SingleMessagingTargetSet set(target);
|
||||
return DeliverMessage(message, set, timeout);
|
||||
}
|
||||
|
||||
// DeliverMessage
|
||||
@ -570,8 +586,7 @@ MessageDeliverer::DeliverMessage(const void *message, int32 messageSize,
|
||||
fails due to a full target port, the message is queued for later delivery.
|
||||
|
||||
\param message The message to be delivered.
|
||||
\param targets An array of BMessengers identifying the delivery targets.
|
||||
\param targetCount The number of delivery targets.
|
||||
\param targets MessagingTargetSet providing the the delivery targets.
|
||||
\param timeout If given, the message will be dropped, when it couldn't be
|
||||
delivered after this amount of microseconds.
|
||||
\return
|
||||
@ -580,29 +595,13 @@ MessageDeliverer::DeliverMessage(const void *message, int32 messageSize,
|
||||
- another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
MessageDeliverer::DeliverMessage(BMessage *message, const BMessenger *targets,
|
||||
int32 targetCount, bigtime_t timeout)
|
||||
MessageDeliverer::DeliverMessage(BMessage *message, MessagingTargetSet &targets,
|
||||
bigtime_t timeout)
|
||||
{
|
||||
if (!message || targetCount < 0 || !targets)
|
||||
if (!message)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// convert the reply targets
|
||||
messaging_target *messagingTargets
|
||||
= new(nothrow) messaging_target[targetCount];
|
||||
if (!messagingTargets)
|
||||
return B_NO_MEMORY;
|
||||
ArrayDeleter<messaging_target> _(messagingTargets);
|
||||
|
||||
for (int i = 0; i < targetCount; i++) {
|
||||
BMessenger messenger(targets[i]);
|
||||
BMessenger::Private messengerPrivate(messenger);
|
||||
messaging_target &target = messagingTargets[i];
|
||||
target.port = messengerPrivate.Port();
|
||||
target.token = messengerPrivate.IsPreferredTarget()
|
||||
? B_PREFERRED_TOKEN : messengerPrivate.Token();
|
||||
}
|
||||
|
||||
// Set a dummy token now, so that the header contains room for it.
|
||||
// Set the token now, so that the header contains room for it.
|
||||
// It will be set when sending the message anyway, but if it is not set
|
||||
// before flattening, the header will not contain room for it, and it
|
||||
// will not possible to send the message flattened later.
|
||||
@ -614,8 +613,8 @@ MessageDeliverer::DeliverMessage(BMessage *message, const BMessenger *targets,
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return DeliverMessage(mallocIO.Buffer(), mallocIO.BufferLength(),
|
||||
messagingTargets, targetCount, timeout);
|
||||
return DeliverMessage(mallocIO.Buffer(), mallocIO.BufferLength(), targets,
|
||||
timeout);
|
||||
}
|
||||
|
||||
// DeliverMessage
|
||||
@ -628,9 +627,7 @@ MessageDeliverer::DeliverMessage(BMessage *message, const BMessenger *targets,
|
||||
\param message The flattened message to be delivered. This may be a
|
||||
flattened BMessage or KMessage.
|
||||
\param messageSize The size of the flattened message buffer.
|
||||
\param targets An array of messaging_targets identifying the delivery
|
||||
targets.
|
||||
\param targetCount The number of delivery targets.
|
||||
\param targets MessagingTargetSet providing the the delivery targets.
|
||||
\param timeout If given, the message will be dropped, when it couldn't be
|
||||
delivered after this amount of microseconds.
|
||||
\return
|
||||
@ -640,7 +637,7 @@ MessageDeliverer::DeliverMessage(BMessage *message, const BMessenger *targets,
|
||||
*/
|
||||
status_t
|
||||
MessageDeliverer::DeliverMessage(const void *messageData, int32 messageSize,
|
||||
const messaging_target *targets, int32 targetCount, bigtime_t timeout)
|
||||
MessagingTargetSet &targets, bigtime_t timeout)
|
||||
{
|
||||
if (!messageData || messageSize <= 0)
|
||||
return B_BAD_VALUE;
|
||||
@ -661,16 +658,19 @@ MessageDeliverer::DeliverMessage(const void *messageData, int32 messageSize,
|
||||
|
||||
// add the message to the respective target ports
|
||||
BAutolock locker(fLock);
|
||||
for (int32 i = 0; i < targetCount; i++) {
|
||||
for (int32 targetIndex = 0; targets.HasNext(); targetIndex++) {
|
||||
port_id portID;
|
||||
int32 token;
|
||||
targets.Next(portID, token);
|
||||
|
||||
// get the target port
|
||||
TargetPort *port = _GetTargetPort(targets[i].port, true);
|
||||
TargetPort *port = _GetTargetPort(portID, true);
|
||||
if (!port)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// try sending the message, if there are no queued messages yet
|
||||
if (port->IsEmpty()) {
|
||||
status_t error = _SendMessage(message, targets[i].port,
|
||||
targets[i].token);
|
||||
status_t error = _SendMessage(message, portID, token);
|
||||
// if the message was delivered OK, we're done with the target
|
||||
if (error == B_OK) {
|
||||
_PutTargetPort(port);
|
||||
@ -680,14 +680,14 @@ MessageDeliverer::DeliverMessage(const void *messageData, int32 messageSize,
|
||||
// if the port is not full, but an error occurred, we skip this target
|
||||
if (error != B_WOULD_BLOCK) {
|
||||
_PutTargetPort(port);
|
||||
if (targetCount == 1)
|
||||
if (targetIndex == 0 && !targets.HasNext())
|
||||
return error;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// add the message
|
||||
status_t error = port->PushMessage(message, targets[i].token);
|
||||
status_t error = port->PushMessage(message, token);
|
||||
_PutTargetPort(port);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
@ -11,6 +11,51 @@
|
||||
|
||||
struct messaging_target;
|
||||
|
||||
// MessagingTargetSet
|
||||
class MessagingTargetSet {
|
||||
public:
|
||||
virtual ~MessagingTargetSet();
|
||||
|
||||
virtual bool HasNext() const = 0;
|
||||
virtual bool Next(port_id &port, int32 &token) = 0;
|
||||
virtual void Rewind() = 0;
|
||||
};
|
||||
|
||||
// DefaultMessagingTargetSet
|
||||
class DefaultMessagingTargetSet : public MessagingTargetSet {
|
||||
public:
|
||||
DefaultMessagingTargetSet(const messaging_target *targets,
|
||||
int32 targetCount);
|
||||
virtual ~DefaultMessagingTargetSet();
|
||||
|
||||
virtual bool HasNext() const;
|
||||
virtual bool Next(port_id &port, int32 &token);
|
||||
virtual void Rewind();
|
||||
|
||||
private:
|
||||
const messaging_target *fTargets;
|
||||
int32 fTargetCount;
|
||||
int32 fNextIndex;
|
||||
};
|
||||
|
||||
// SingleMessagingTargetSet
|
||||
class SingleMessagingTargetSet : public MessagingTargetSet {
|
||||
public:
|
||||
SingleMessagingTargetSet(BMessenger target);
|
||||
SingleMessagingTargetSet(port_id port, int32 token);
|
||||
virtual ~SingleMessagingTargetSet();
|
||||
|
||||
virtual bool HasNext() const;
|
||||
virtual bool Next(port_id &port, int32 &token);
|
||||
virtual void Rewind();
|
||||
|
||||
private:
|
||||
port_id fPort;
|
||||
int32 fToken;
|
||||
bool fAtBeginning;
|
||||
};
|
||||
|
||||
// MessageDeliverer
|
||||
class MessageDeliverer {
|
||||
private:
|
||||
MessageDeliverer();
|
||||
@ -25,19 +70,10 @@ public:
|
||||
|
||||
status_t DeliverMessage(BMessage *message, BMessenger target,
|
||||
bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
status_t DeliverMessage(BMessage *message, port_id port, int32 token,
|
||||
status_t DeliverMessage(BMessage *message, MessagingTargetSet &targets,
|
||||
bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
status_t DeliverMessage(const void *message, int32 messageSize,
|
||||
BMessenger target, bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
status_t DeliverMessage(const void *message, int32 messageSize,
|
||||
port_id port, int32 token, bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
|
||||
status_t DeliverMessage(BMessage *message, const BMessenger *targets,
|
||||
int32 targetCount,
|
||||
bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
status_t DeliverMessage(const void *message, int32 messageSize,
|
||||
const messaging_target *targets, int32 targetCount,
|
||||
bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
MessagingTargetSet &targets, bigtime_t timeout = B_INFINITE_TIMEOUT);
|
||||
|
||||
private:
|
||||
class Message;
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
|
||||
virtual bool Do(EventQueue *queue);
|
||||
|
||||
private:
|
||||
protected:
|
||||
BMessage fMessage;
|
||||
BMessenger fMessenger;
|
||||
BHandler *fHandler;
|
||||
|
@ -186,8 +186,10 @@ class MessagingService::DefaultSendCommandHandler
|
||||
+ sizeof(messaging_command_send_message)
|
||||
+ sizeof(messaging_target) * sendData->target_count;
|
||||
|
||||
DefaultMessagingTargetSet set(sendData->targets,
|
||||
sendData->target_count);
|
||||
MessageDeliverer::Default()->DeliverMessage(messageData,
|
||||
sendData->message_size, sendData->targets, sendData->target_count);
|
||||
sendData->message_size, set);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36,9 +36,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
namespace BPrivate {
|
||||
class TRoster;
|
||||
}
|
||||
|
||||
struct entry_ref;
|
||||
|
||||
@ -56,7 +54,7 @@ public:
|
||||
|
||||
static const int32 kQualifyingAppFlags = 0;
|
||||
private:
|
||||
friend class BPrivate::TRoster;
|
||||
friend class TRoster;
|
||||
// For loading from disk
|
||||
|
||||
static status_t GetRefForApp(const char *appSig, entry_ref *result);
|
||||
|
@ -37,9 +37,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
namespace BPrivate {
|
||||
class TRoster;
|
||||
}
|
||||
|
||||
struct recent_entry {
|
||||
recent_entry(const entry_ref *ref, const char *appSig, uint32 index);
|
||||
@ -62,7 +60,7 @@ public:
|
||||
status_t Print();
|
||||
status_t Save(FILE* file, const char *description, const char *tag);
|
||||
private:
|
||||
friend class BPrivate::TRoster;
|
||||
friend class TRoster;
|
||||
|
||||
static status_t GetTypeForRef(const entry_ref *ref, char *result);
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "MessagingService.h"
|
||||
#include "MIMEManager.h"
|
||||
#include "Registrar.h"
|
||||
#include "ShutdownProcess.h"
|
||||
#include "TRoster.h"
|
||||
|
||||
/*!
|
||||
@ -98,6 +99,7 @@ Registrar::MessageReceived(BMessage *message)
|
||||
message->SendReply(&reply);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_REG_GET_CLIPBOARD_MESSENGER:
|
||||
{
|
||||
PRINT(("B_REG_GET_CLIPBOARD_MESSENGER\n"));
|
||||
@ -107,6 +109,14 @@ Registrar::MessageReceived(BMessage *message)
|
||||
message->SendReply(&reply);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_REG_SHUT_DOWN:
|
||||
{
|
||||
PRINT(("B_REG_SHUT_DOWN\n"));
|
||||
|
||||
_HandleShutDown(message);
|
||||
}
|
||||
|
||||
// roster requests
|
||||
case B_REG_ADD_APP:
|
||||
fRoster->HandleAddApplication(message);
|
||||
@ -180,6 +190,7 @@ Registrar::MessageReceived(BMessage *message)
|
||||
case B_REG_SAVE_RECENT_LISTS:
|
||||
fRoster->HandleSaveRecentLists(message);
|
||||
break;
|
||||
|
||||
// message runner requests
|
||||
case B_REG_REGISTER_MESSAGE_RUNNER:
|
||||
fMessageRunnerManager->HandleRegisterRunner(message);
|
||||
@ -193,12 +204,22 @@ Registrar::MessageReceived(BMessage *message)
|
||||
case B_REG_GET_MESSAGE_RUNNER_INFO:
|
||||
fMessageRunnerManager->HandleGetRunnerInfo(message);
|
||||
break;
|
||||
|
||||
// internal messages
|
||||
case B_REG_ROSTER_SANITY_EVENT:
|
||||
fRoster->CheckSanity();
|
||||
fSanityEvent->SetTime(system_time() + kRosterSanityEventInterval);
|
||||
fEventQueue->AddEvent(fSanityEvent);
|
||||
break;
|
||||
case B_REG_SHUTDOWN_FINISHED:
|
||||
if (fShutdownProcess) {
|
||||
if (fShutdownProcess->Lock()) {
|
||||
fShutdownProcess->Quit();
|
||||
fShutdownProcess = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
BApplication::MessageReceived(message);
|
||||
break;
|
||||
@ -291,6 +312,40 @@ Registrar::App()
|
||||
return dynamic_cast<Registrar*>(be_app);
|
||||
}
|
||||
|
||||
// _HandleShutDown
|
||||
/*! \brief Handle a shut down request message.
|
||||
\param request The request to be handled.
|
||||
*/
|
||||
void
|
||||
Registrar::_HandleShutDown(BMessage *request)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
|
||||
// check, whether we're already shutting down
|
||||
if (fShutdownProcess)
|
||||
error = B_SHUTTING_DOWN;
|
||||
|
||||
bool needsReply = true;
|
||||
if (error == B_OK) {
|
||||
// create a ShutdownProcess
|
||||
fShutdownProcess = new(nothrow) ShutdownProcess(fRoster, fEventQueue);
|
||||
if (fShutdownProcess) {
|
||||
error = fShutdownProcess->Init(request);
|
||||
if (error == B_OK) {
|
||||
DetachCurrentMessage();
|
||||
fShutdownProcess->Run();
|
||||
needsReply = false;
|
||||
} else {
|
||||
delete fShutdownProcess;
|
||||
fShutdownProcess = NULL;
|
||||
}
|
||||
} else
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (needsReply)
|
||||
ShutdownProcess::SendReply(request, error);
|
||||
}
|
||||
|
||||
// main
|
||||
/*! \brief Creates and runs the registrar application.
|
||||
|
@ -34,10 +34,9 @@ class EventQueue;
|
||||
class MessageEvent;
|
||||
class MessageRunnerManager;
|
||||
class MIMEManager;
|
||||
class ShutdownProcess;
|
||||
|
||||
namespace BPrivate {
|
||||
class TRoster;
|
||||
};
|
||||
|
||||
class Registrar : public BServer {
|
||||
public:
|
||||
@ -53,12 +52,15 @@ public:
|
||||
static Registrar *App();
|
||||
|
||||
private:
|
||||
BPrivate::TRoster *fRoster;
|
||||
void _HandleShutDown(BMessage *message);
|
||||
|
||||
TRoster *fRoster;
|
||||
ClipboardHandler *fClipboardHandler;
|
||||
MIMEManager *fMIMEManager;
|
||||
EventQueue *fEventQueue;
|
||||
MessageRunnerManager *fMessageRunnerManager;
|
||||
MessageEvent *fSanityEvent;
|
||||
ShutdownProcess *fShutdownProcess;
|
||||
};
|
||||
|
||||
#endif // REGISTRAR_H
|
||||
|
@ -24,6 +24,8 @@
|
||||
// Description: An extended app_info.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "RosterAppInfo.h"
|
||||
@ -54,3 +56,14 @@ RosterAppInfo::Init(thread_id thread, team_id team, port_id port, uint32 flags,
|
||||
this->signature[0] = '\0';
|
||||
}
|
||||
|
||||
// Clone
|
||||
RosterAppInfo *
|
||||
RosterAppInfo::Clone() const
|
||||
{
|
||||
RosterAppInfo *clone = new(nothrow) RosterAppInfo;
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
clone->Init(thread, team, port, flags, &ref, signature);
|
||||
return clone;
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ struct RosterAppInfo : app_info {
|
||||
RosterAppInfo();
|
||||
void Init(thread_id thread, team_id team, port_id port, uint32 flags,
|
||||
const entry_ref *ref, const char *signature);
|
||||
|
||||
RosterAppInfo *Clone() const;
|
||||
};
|
||||
|
||||
#endif // ROSTER_APP_INFO_H
|
||||
|
922
src/servers/registrar/ShutdownProcess.cpp
Normal file
922
src/servers/registrar/ShutdownProcess.cpp
Normal file
@ -0,0 +1,922 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
// TODO: While debugging only. Remove when implementation is done.
|
||||
#ifdef DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
#define DEBUG 1
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <Message.h>
|
||||
#include <MessagePrivate.h>
|
||||
#include <RegistrarDefs.h>
|
||||
#include <Roster.h> // for B_REQUEST_QUIT
|
||||
|
||||
#include <TokenSpace.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include "AppInfoListMessagingTargetSet.h"
|
||||
#include "Debug.h"
|
||||
#include "EventQueue.h"
|
||||
#include "MessageDeliverer.h"
|
||||
#include "MessageEvent.h"
|
||||
#include "Registrar.h"
|
||||
#include "RosterAppInfo.h"
|
||||
#include "ShutdownProcess.h"
|
||||
#include "TRoster.h"
|
||||
|
||||
using namespace BPrivate;
|
||||
|
||||
// The time span user applications have after the quit message has been
|
||||
// delivered (more precisely: has been handed over to the MessageDeliverer).
|
||||
static const bigtime_t USER_APPS_QUIT_TIMEOUT = 5000000; // 5 s
|
||||
|
||||
// The time span system applications have after the quit message has been
|
||||
// delivered (more precisely: has been handed over to the MessageDeliverer).
|
||||
static const bigtime_t SYSTEM_APPS_QUIT_TIMEOUT = 3000000; // 3 s
|
||||
|
||||
// The time span non-app processes have after the HUP signal has been send
|
||||
// to them before they get a KILL signal.
|
||||
static const bigtime_t NON_APPS_QUIT_TIMEOUT = 500000; // 0.5 s
|
||||
|
||||
|
||||
// message what fields
|
||||
enum {
|
||||
MSG_PHASE_TIMED_OUT = 'phto',
|
||||
MSG_DONE = 'done',
|
||||
};
|
||||
|
||||
// internal events
|
||||
enum {
|
||||
NO_EVENT,
|
||||
ABORT_EVENT,
|
||||
TIMEOUT_EVENT,
|
||||
USER_APP_QUIT_EVENT,
|
||||
SYSTEM_APP_QUIT_EVENT,
|
||||
};
|
||||
|
||||
// phases
|
||||
enum {
|
||||
INVALID_PHASE = -1,
|
||||
USER_APP_TERMINATION_PHASE = 0,
|
||||
SYSTEM_APP_TERMINATION_PHASE = 1,
|
||||
OTHER_PROCESSES_TERMINATION_PHASE = 2,
|
||||
DONE_PHASE = 3,
|
||||
ABORTED_PHASE = 4,
|
||||
};
|
||||
|
||||
// TimeoutEvent
|
||||
class ShutdownProcess::TimeoutEvent : public MessageEvent {
|
||||
public:
|
||||
TimeoutEvent(BHandler *target)
|
||||
: MessageEvent(0, target, MSG_PHASE_TIMED_OUT)
|
||||
{
|
||||
SetAutoDelete(false);
|
||||
|
||||
fMessage.AddInt32("phase", INVALID_PHASE);
|
||||
fMessage.AddInt32("team", -1);
|
||||
}
|
||||
|
||||
void SetPhase(int32 phase)
|
||||
{
|
||||
fMessage.ReplaceInt32("phase", phase);
|
||||
}
|
||||
|
||||
void SetTeam(team_id team)
|
||||
{
|
||||
fMessage.ReplaceInt32("team", team);
|
||||
}
|
||||
|
||||
static int32 GetMessagePhase(BMessage *message)
|
||||
{
|
||||
int32 phase;
|
||||
if (message->FindInt32("phase", &phase) != B_OK)
|
||||
phase = INVALID_PHASE;
|
||||
|
||||
return phase;
|
||||
}
|
||||
|
||||
static int32 GetMessageTeam(BMessage *message)
|
||||
{
|
||||
team_id team;
|
||||
if (message->FindInt32("team", &team) != B_OK)
|
||||
team = -1;
|
||||
|
||||
return team;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// InternalEvent
|
||||
class ShutdownProcess::InternalEvent
|
||||
: public DoublyLinkedListLinkImpl<InternalEvent> {
|
||||
public:
|
||||
InternalEvent(uint32 type, team_id team, int32 phase)
|
||||
: fType(type),
|
||||
fTeam(team),
|
||||
fPhase(phase)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 Type() const { return fType; }
|
||||
team_id Team() const { return fTeam; }
|
||||
int32 Phase() const { return fPhase; }
|
||||
|
||||
private:
|
||||
uint32 fType;
|
||||
int32 fTeam;
|
||||
int32 fPhase;
|
||||
};
|
||||
|
||||
|
||||
// InternalEventList
|
||||
struct ShutdownProcess::InternalEventList : DoublyLinkedList<InternalEvent> {
|
||||
};
|
||||
|
||||
|
||||
// QuitRequestReplyHandler
|
||||
class ShutdownProcess::QuitRequestReplyHandler : public BHandler {
|
||||
public:
|
||||
QuitRequestReplyHandler(ShutdownProcess *shutdownProcess)
|
||||
: BHandler("shutdown quit reply handler"),
|
||||
fShutdownProcess(shutdownProcess)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void MessageReceived(BMessage *message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case B_REPLY:
|
||||
{
|
||||
bool result;
|
||||
thread_id thread;
|
||||
if (message->FindBool("result", &result) == B_OK
|
||||
&& message->FindInt32("thread", &thread) == B_OK) {
|
||||
if (!result)
|
||||
fShutdownProcess->_NegativeQuitRequestReply(thread);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BHandler::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ShutdownProcess *fShutdownProcess;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// constructor
|
||||
ShutdownProcess::ShutdownProcess(TRoster *roster, EventQueue *eventQueue)
|
||||
: BLooper("shutdown process"),
|
||||
EventMaskWatcher(BMessenger(this), B_REQUEST_QUIT),
|
||||
fWorkerLock("worker lock"),
|
||||
fRequest(NULL),
|
||||
fRoster(roster),
|
||||
fEventQueue(eventQueue),
|
||||
fTimeoutEvent(NULL),
|
||||
fInternalEvents(NULL),
|
||||
fInternalEventSemaphore(-1),
|
||||
fQuitRequestReplyHandler(NULL),
|
||||
fWorker(-1),
|
||||
fCurrentPhase(INVALID_PHASE),
|
||||
fShutdownError(B_ERROR),
|
||||
fHasGUI(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
ShutdownProcess::~ShutdownProcess()
|
||||
{
|
||||
// remove and delete the quit request reply handler
|
||||
if (fQuitRequestReplyHandler) {
|
||||
BAutolock _(this);
|
||||
RemoveHandler(fQuitRequestReplyHandler);
|
||||
delete fQuitRequestReplyHandler;
|
||||
}
|
||||
|
||||
// remove and delete the timeout event
|
||||
if (fTimeoutEvent) {
|
||||
fEventQueue->RemoveEvent(fTimeoutEvent);
|
||||
|
||||
delete fTimeoutEvent;
|
||||
}
|
||||
|
||||
// delete the internal event semaphore
|
||||
if (fInternalEventSemaphore >= 0)
|
||||
delete_sem(fInternalEventSemaphore);
|
||||
|
||||
// wait for the worker thread to terminate
|
||||
if (fWorker >= 0) {
|
||||
int32 result;
|
||||
wait_for_thread(fWorker, &result);
|
||||
}
|
||||
|
||||
// delete all internal events and the queue
|
||||
if (fInternalEvents) {
|
||||
while (InternalEvent *event = fInternalEvents->First()) {
|
||||
fInternalEvents->Remove(event);
|
||||
delete event;
|
||||
}
|
||||
|
||||
delete fInternalEvents;
|
||||
}
|
||||
|
||||
// send a reply to the request and delete it
|
||||
SendReply(fRequest, fShutdownError);
|
||||
delete fRequest;
|
||||
}
|
||||
|
||||
// Init
|
||||
status_t
|
||||
ShutdownProcess::Init(BMessage *request)
|
||||
{
|
||||
PRINT(("ShutdownProcess::Init()\n"));
|
||||
|
||||
// create and add the quit request reply handler
|
||||
fQuitRequestReplyHandler = new(nothrow) QuitRequestReplyHandler(this);
|
||||
if (!fQuitRequestReplyHandler)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
AddHandler(fQuitRequestReplyHandler);
|
||||
|
||||
// create the timeout event
|
||||
fTimeoutEvent = new(nothrow) TimeoutEvent(this);
|
||||
if (!fTimeoutEvent)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
|
||||
// create the event list
|
||||
fInternalEvents = new(nothrow) InternalEventList;
|
||||
if (!fInternalEvents)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
|
||||
// create the event sempahore
|
||||
fInternalEventSemaphore = create_sem(0, "shutdown events");
|
||||
if (fInternalEventSemaphore < 0)
|
||||
RETURN_ERROR(fInternalEventSemaphore);
|
||||
|
||||
// init the app server connection
|
||||
fHasGUI = (Registrar::App()->InitGUIContext() == B_OK);
|
||||
|
||||
// tell TRoster not to accept new applications anymore
|
||||
fRoster->SetShuttingDown(true);
|
||||
|
||||
// start watching application quits
|
||||
status_t error = fRoster->AddWatcher(this);
|
||||
if (error != B_OK) {
|
||||
fRoster->SetShuttingDown(false);
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// get a list of all applications to shut down and sort them
|
||||
error = fRoster->GetShutdownApps(fUserApps, fSystemApps, fVitalSystemApps);
|
||||
if (error != B_OK) {
|
||||
fRoster->RemoveWatcher(this);
|
||||
fRoster->SetShuttingDown(false);
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
// TODO: sort the system apps by descending registration time
|
||||
|
||||
// display alert
|
||||
// TODO:...
|
||||
|
||||
// start the worker thread
|
||||
fWorker = spawn_thread(_WorkerEntry, "shutdown worker", B_NORMAL_PRIORITY,
|
||||
this);
|
||||
if (fWorker < 0) {
|
||||
fRoster->RemoveWatcher(this);
|
||||
fRoster->SetShuttingDown(false);
|
||||
RETURN_ERROR(fWorker);
|
||||
}
|
||||
|
||||
// everything went fine: now we own the request
|
||||
fRequest = request;
|
||||
|
||||
resume_thread(fWorker);
|
||||
|
||||
PRINT(("ShutdownProcess::Init() done\n"));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// MessageReceived
|
||||
void
|
||||
ShutdownProcess::MessageReceived(BMessage *message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case B_SOME_APP_QUIT:
|
||||
{
|
||||
// get the team
|
||||
team_id team;
|
||||
if (message->FindInt32("be:team", &team) != B_OK) {
|
||||
// should not happen
|
||||
return;
|
||||
}
|
||||
|
||||
PRINT(("ShutdownProcess::MessageReceived(): B_SOME_APP_QUIT: %ld\n",
|
||||
team));
|
||||
|
||||
// remove the app info from the respective list
|
||||
_LockAppLists();
|
||||
|
||||
uint32 event;
|
||||
|
||||
RosterAppInfo *info = fUserApps.InfoFor(team);
|
||||
if (info) {
|
||||
fUserApps.RemoveInfo(info);
|
||||
event = USER_APP_QUIT_EVENT;
|
||||
} else if ((info = fSystemApps.InfoFor(team))) {
|
||||
fSystemApps.RemoveInfo(info);
|
||||
event = SYSTEM_APP_QUIT_EVENT;
|
||||
} else // not found
|
||||
return;
|
||||
|
||||
int32 phase = fCurrentPhase;
|
||||
|
||||
_UnlockAppLists();
|
||||
|
||||
// post the event
|
||||
_PushEvent(event, team, phase);
|
||||
|
||||
delete info;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_PHASE_TIMED_OUT:
|
||||
{
|
||||
// get the phase the event is intended for
|
||||
int32 phase = TimeoutEvent::GetMessagePhase(message);
|
||||
team_id team = TimeoutEvent::GetMessageTeam(message);;
|
||||
|
||||
BAutolock _(fWorkerLock);
|
||||
|
||||
if (phase == INVALID_PHASE || phase != fCurrentPhase)
|
||||
return;
|
||||
|
||||
// post the event
|
||||
_PushEvent(TIMEOUT_EVENT, team, phase);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_DONE:
|
||||
{
|
||||
// notify the registrar that we're done
|
||||
be_app->PostMessage(B_REG_SHUTDOWN_FINISHED, be_app);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BLooper::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SendReply
|
||||
void
|
||||
ShutdownProcess::SendReply(BMessage *request, status_t error)
|
||||
{
|
||||
if (error == B_OK) {
|
||||
BMessage reply(B_REG_SUCCESS);
|
||||
request->SendReply(&reply);
|
||||
} else {
|
||||
BMessage reply(B_REG_ERROR);
|
||||
reply.AddInt32("error", error);
|
||||
request->SendReply(&reply);
|
||||
}
|
||||
}
|
||||
|
||||
// _SetPhase
|
||||
void
|
||||
ShutdownProcess::_SetPhase(int32 phase)
|
||||
{
|
||||
BAutolock _(fWorkerLock);
|
||||
|
||||
if (phase == fCurrentPhase)
|
||||
return;
|
||||
|
||||
fCurrentPhase = phase;
|
||||
|
||||
// remove the timeout event scheduled for the previous phase
|
||||
fEventQueue->RemoveEvent(fTimeoutEvent);
|
||||
}
|
||||
|
||||
// _ScheduleTimeoutEvent
|
||||
void
|
||||
ShutdownProcess::_ScheduleTimeoutEvent(bigtime_t timeout, team_id team)
|
||||
{
|
||||
BAutolock _(fWorkerLock);
|
||||
|
||||
// remove the timeout event
|
||||
fEventQueue->RemoveEvent(fTimeoutEvent);
|
||||
|
||||
// set the event's phase, team and time
|
||||
fTimeoutEvent->SetPhase(fCurrentPhase);
|
||||
fTimeoutEvent->SetTeam(team);
|
||||
fTimeoutEvent->SetTime(system_time() + timeout);
|
||||
|
||||
// add the event
|
||||
fEventQueue->AddEvent(fTimeoutEvent);
|
||||
}
|
||||
|
||||
// _LockAppLists
|
||||
bool
|
||||
ShutdownProcess::_LockAppLists()
|
||||
{
|
||||
return fWorkerLock.Lock();
|
||||
}
|
||||
|
||||
// _UnlockAppLists
|
||||
void
|
||||
ShutdownProcess::_UnlockAppLists()
|
||||
{
|
||||
fWorkerLock.Unlock();
|
||||
}
|
||||
|
||||
// _NegativeQuitRequestReply
|
||||
void
|
||||
ShutdownProcess::_NegativeQuitRequestReply(thread_id thread)
|
||||
{
|
||||
BAutolock _(fWorkerLock);
|
||||
|
||||
// Note: team ID == team main thread ID under Haiku. When testing under R5
|
||||
// using the team ID in case of an ABORT_EVENT won't work correctly. But
|
||||
// this is done only for system apps.
|
||||
_PushEvent(ABORT_EVENT, thread, fCurrentPhase);
|
||||
}
|
||||
|
||||
// _PrepareShutdownMessage
|
||||
void
|
||||
ShutdownProcess::_PrepareShutdownMessage(BMessage &message) const
|
||||
{
|
||||
message.what = B_QUIT_REQUESTED;
|
||||
message.AddBool("_shutdown_", true);
|
||||
|
||||
BMessage::Private(message).SetReply(BMessenger(fQuitRequestReplyHandler));
|
||||
}
|
||||
|
||||
// _ShutDown
|
||||
status_t
|
||||
ShutdownProcess::_ShutDown()
|
||||
{
|
||||
// get the reboot flag
|
||||
bool reboot;
|
||||
if (fRequest->FindBool("reboot", &reboot) != B_OK)
|
||||
reboot = false;
|
||||
|
||||
#ifdef __HAIKU__
|
||||
return _kern_shutdown(reboot);
|
||||
#else
|
||||
// we can't do anything on R5
|
||||
return B_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// _PushEvent
|
||||
status_t
|
||||
ShutdownProcess::_PushEvent(uint32 eventType, team_id team, int32 phase)
|
||||
{
|
||||
InternalEvent *event = new(nothrow) InternalEvent(eventType, team, phase);
|
||||
if (!event) {
|
||||
ERROR(("ShutdownProcess::_PushEvent(): Failed to create event!\n"));
|
||||
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
BAutolock _(fWorkerLock);
|
||||
|
||||
fInternalEvents->Add(event);
|
||||
release_sem(fInternalEventSemaphore);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// _GetNextEvent
|
||||
status_t
|
||||
ShutdownProcess::_GetNextEvent(uint32 &eventType, thread_id &team, int32 &phase,
|
||||
bool block)
|
||||
{
|
||||
while (true) {
|
||||
// acquire the semaphore
|
||||
if (block) {
|
||||
status_t error;
|
||||
do {
|
||||
error = acquire_sem(fInternalEventSemaphore);
|
||||
} while (error == B_INTERRUPTED);
|
||||
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
} else {
|
||||
status_t error = acquire_sem_etc(fInternalEventSemaphore, 1,
|
||||
B_RELATIVE_TIMEOUT, 0);
|
||||
if (error != B_OK) {
|
||||
eventType = NO_EVENT;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// get the event
|
||||
BAutolock _(fWorkerLock);
|
||||
|
||||
InternalEvent *event = fInternalEvents->Head();
|
||||
fInternalEvents->Remove(event);
|
||||
|
||||
eventType = event->Type();
|
||||
team = event->Team();
|
||||
phase = event->Phase();
|
||||
|
||||
delete event;
|
||||
|
||||
// if the event is an obsolete timeout event, we drop it right here
|
||||
if (eventType == TIMEOUT_EVENT && phase != fCurrentPhase)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// _WorkerEntry
|
||||
status_t
|
||||
ShutdownProcess::_WorkerEntry(void *data)
|
||||
{
|
||||
return ((ShutdownProcess*)data)->_Worker();
|
||||
}
|
||||
|
||||
// _Worker
|
||||
status_t
|
||||
ShutdownProcess::_Worker()
|
||||
{
|
||||
try {
|
||||
_WorkerDoShutdown();
|
||||
fShutdownError = B_OK;
|
||||
} catch (status_t error) {
|
||||
PRINT(("ShutdownProcess::_Worker(): caught exception: %s\n",
|
||||
strerror(error)));
|
||||
|
||||
fShutdownError = error;
|
||||
}
|
||||
|
||||
// this can happen only, if the shutdown process failed or was aborted:
|
||||
// notify the looper
|
||||
_SetPhase(DONE_PHASE);
|
||||
PostMessage(MSG_DONE);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// _WorkerDoShutdown
|
||||
void
|
||||
ShutdownProcess::_WorkerDoShutdown()
|
||||
{
|
||||
PRINT(("ShutdownProcess::_WorkerDoShutdown()\n"));
|
||||
|
||||
// TODO: Set alert text to "Tidying things up a bit".
|
||||
PRINT((" sync()...\n"));
|
||||
sync();
|
||||
|
||||
// phase 1: terminate the user apps
|
||||
_SetPhase(USER_APP_TERMINATION_PHASE);
|
||||
_QuitUserApps();
|
||||
_ScheduleTimeoutEvent(USER_APPS_QUIT_TIMEOUT);
|
||||
_WaitForUserApps();
|
||||
_KillUserApps();
|
||||
|
||||
// phase 2: terminate the system apps
|
||||
_SetPhase(SYSTEM_APP_TERMINATION_PHASE);
|
||||
_QuitSystemApps();
|
||||
|
||||
// phase 3: terminate the other processes
|
||||
_SetPhase(OTHER_PROCESSES_TERMINATION_PHASE);
|
||||
_QuitNonApps();
|
||||
|
||||
// we're through: do the shutdown
|
||||
_SetPhase(DONE_PHASE);
|
||||
_ShutDown();
|
||||
|
||||
PRINT((" _kern_shutdown() failed\n"));
|
||||
|
||||
// shutdown failed: This can happen for power off mode -- reboot will
|
||||
// always work. We close the alert, and display a new one with
|
||||
// a "Reboot System" button.
|
||||
if (fHasGUI) {
|
||||
// TODO:...
|
||||
} else {
|
||||
// there's no GUI: we enter the kernel debugger instead
|
||||
// TODO:...
|
||||
}
|
||||
}
|
||||
|
||||
// _QuitUserApps
|
||||
void
|
||||
ShutdownProcess::_QuitUserApps()
|
||||
{
|
||||
PRINT(("ShutdownProcess::_QuitUserApps()\n"));
|
||||
|
||||
// TODO: Set alert text to "Asking user applications to quit"
|
||||
|
||||
// prepare the shutdown message
|
||||
BMessage message;
|
||||
_PrepareShutdownMessage(message);
|
||||
|
||||
// send shutdown messages to user apps
|
||||
_LockAppLists();
|
||||
|
||||
AppInfoListMessagingTargetSet targetSet(fUserApps);
|
||||
|
||||
if (targetSet.HasNext()) {
|
||||
PRINT((" sending shutdown message to %ld apps\n",
|
||||
fUserApps.CountInfos()));
|
||||
|
||||
status_t error = MessageDeliverer::Default()->DeliverMessage(
|
||||
&message, targetSet);
|
||||
if (error != B_OK) {
|
||||
WARNING(("ShutdownProcess::_Worker(): Failed to deliver "
|
||||
"shutdown message to all applications: %s\n",
|
||||
strerror(error)));
|
||||
}
|
||||
}
|
||||
|
||||
_UnlockAppLists();
|
||||
|
||||
PRINT(("ShutdownProcess::_QuitUserApps() done\n"));
|
||||
}
|
||||
|
||||
// _WaitForUserApps
|
||||
void
|
||||
ShutdownProcess::_WaitForUserApps()
|
||||
{
|
||||
PRINT(("ShutdownProcess::_WaitForUserApps()\n"));
|
||||
|
||||
// wait for user apps
|
||||
bool moreApps = true;
|
||||
while (moreApps) {
|
||||
_LockAppLists();
|
||||
moreApps = !fUserApps.IsEmpty();
|
||||
_UnlockAppLists();
|
||||
|
||||
if (moreApps) {
|
||||
uint32 event;
|
||||
team_id team;
|
||||
int32 phase;
|
||||
status_t error = _GetNextEvent(event, team, phase, true);
|
||||
if (error != B_OK)
|
||||
throw error;
|
||||
|
||||
if (event == ABORT_EVENT)
|
||||
throw B_SHUTDOWN_CANCELLED;
|
||||
|
||||
if (event == TIMEOUT_EVENT)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT(("ShutdownProcess::_WaitForUserApps() done\n"));
|
||||
}
|
||||
|
||||
// _KillUserApps
|
||||
void
|
||||
ShutdownProcess::_KillUserApps()
|
||||
{
|
||||
PRINT(("ShutdownProcess::_KillUserApps()\n"));
|
||||
|
||||
while (true) {
|
||||
// eat events (we need to be responsive for an abort event)
|
||||
uint32 event;
|
||||
do {
|
||||
team_id team;
|
||||
int32 phase;
|
||||
status_t error = _GetNextEvent(event, team, phase, false);
|
||||
if (error != B_OK)
|
||||
throw error;
|
||||
|
||||
if (event == ABORT_EVENT)
|
||||
throw B_SHUTDOWN_CANCELLED;
|
||||
|
||||
} while (event != NO_EVENT);
|
||||
|
||||
// get the first team to kill
|
||||
_LockAppLists();
|
||||
|
||||
team_id team = -1;
|
||||
AppInfoList &list = fUserApps;
|
||||
if (!list.IsEmpty()) {
|
||||
RosterAppInfo *info = *list.It();
|
||||
team = info->team;
|
||||
}
|
||||
|
||||
_UnlockAppLists();
|
||||
|
||||
if (team < 0) {
|
||||
PRINT(("ShutdownProcess::_KillUserApps() done\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Set alert text...
|
||||
|
||||
// TODO: check whether the app blocks on a modal alert
|
||||
if (false) {
|
||||
// ...
|
||||
} else {
|
||||
// it does not: kill it
|
||||
PRINT((" killing team %ld\n", team));
|
||||
|
||||
kill_team(team);
|
||||
|
||||
// remove the app (the roster will note eventually and send us
|
||||
// a notification, but we want to be sure)
|
||||
_LockAppLists();
|
||||
|
||||
if (RosterAppInfo *info = list.InfoFor(team)) {
|
||||
list.RemoveInfo(info);
|
||||
delete info;
|
||||
}
|
||||
|
||||
_UnlockAppLists();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _QuitSystemApps
|
||||
void
|
||||
ShutdownProcess::_QuitSystemApps()
|
||||
{
|
||||
PRINT(("ShutdownProcess::_QuitSystemApps()\n"));
|
||||
|
||||
// TODO: Disable the abort button.
|
||||
|
||||
// check one last time for abort events
|
||||
uint32 event;
|
||||
do {
|
||||
team_id team;
|
||||
int32 phase;
|
||||
status_t error = _GetNextEvent(event, team, phase, false);
|
||||
if (error != B_OK)
|
||||
throw error;
|
||||
|
||||
if (event == ABORT_EVENT)
|
||||
throw B_SHUTDOWN_CANCELLED;
|
||||
|
||||
} while (event != NO_EVENT);
|
||||
|
||||
// prepare the shutdown message
|
||||
BMessage message;
|
||||
_PrepareShutdownMessage(message);
|
||||
|
||||
// now iterate through the list of system apps
|
||||
while (true) {
|
||||
// eat events
|
||||
uint32 event;
|
||||
do {
|
||||
team_id team;
|
||||
int32 phase;
|
||||
status_t error = _GetNextEvent(event, team, phase, false);
|
||||
if (error != B_OK)
|
||||
throw error;
|
||||
} while (event != NO_EVENT);
|
||||
|
||||
// get the first app to quit
|
||||
_LockAppLists();
|
||||
|
||||
AppInfoList &list = fSystemApps;
|
||||
team_id team = -1;
|
||||
port_id port = -1;
|
||||
if (!list.IsEmpty()) {
|
||||
RosterAppInfo *info = *list.It();
|
||||
team = info->team;
|
||||
port = info->port;
|
||||
}
|
||||
|
||||
_UnlockAppLists();
|
||||
|
||||
if (team < 0) {
|
||||
PRINT(("ShutdownProcess::_QuitSystemApps() done\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Set alert text...
|
||||
|
||||
// send the shutdown message to the app
|
||||
PRINT((" sending team %ld (port: %ld) a shutdown message\n", team,
|
||||
port));
|
||||
SingleMessagingTargetSet target(port, B_PREFERRED_TOKEN);
|
||||
MessageDeliverer::Default()->DeliverMessage(&message, target);
|
||||
|
||||
// schedule a timeout event
|
||||
_ScheduleTimeoutEvent(SYSTEM_APPS_QUIT_TIMEOUT, team);
|
||||
|
||||
// wait for the app to die or for the timeout to occur
|
||||
do {
|
||||
team_id eventTeam;
|
||||
int32 phase;
|
||||
status_t error = _GetNextEvent(event, eventTeam, phase, true);
|
||||
if (error != B_OK)
|
||||
throw error;
|
||||
|
||||
if (event == TIMEOUT_EVENT && eventTeam == team)
|
||||
break;
|
||||
|
||||
// If the app requests aborting the shutdown, we don't need to
|
||||
// wait any longer. It has processed the request and won't quit by
|
||||
// itself. We'll have to kill it.
|
||||
if (event == ABORT_EVENT && eventTeam == team)
|
||||
break;
|
||||
|
||||
BAutolock _(fWorkerLock);
|
||||
if (!list.InfoFor(team))
|
||||
break;
|
||||
|
||||
} while (event != NO_EVENT);
|
||||
|
||||
// TODO: Set alert text...
|
||||
|
||||
// TODO: check whether the app blocks on a modal alert
|
||||
if (false) {
|
||||
// ...
|
||||
} else {
|
||||
// it does not: kill it
|
||||
PRINT((" killing team %ld\n", team));
|
||||
|
||||
kill_team(team);
|
||||
|
||||
// remove the app (the roster will note eventually and send us
|
||||
// a notification, but we want to be sure)
|
||||
_LockAppLists();
|
||||
|
||||
if (RosterAppInfo *info = list.InfoFor(team)) {
|
||||
list.RemoveInfo(info);
|
||||
delete info;
|
||||
}
|
||||
|
||||
_UnlockAppLists();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _QuitNonApps
|
||||
void
|
||||
ShutdownProcess::_QuitNonApps()
|
||||
{
|
||||
PRINT(("ShutdownProcess::_QuitNonApps()\n"));
|
||||
|
||||
// TODO: Set alert text...
|
||||
|
||||
// iterate through the remaining teams and send them the HUP signal
|
||||
int32 cookie = 0;
|
||||
team_info teamInfo;
|
||||
while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
|
||||
if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
|
||||
PRINT((" sending team %ld HUP signal\n", teamInfo.team));
|
||||
|
||||
#ifdef __HAIKU__
|
||||
// Note: team ID == team main thread ID under Haiku
|
||||
send_signal(teamInfo.team, SIGHUP);
|
||||
#else
|
||||
// We don't want to do this when testing under R5, since it
|
||||
// would kill all teams besides our app server and registrar.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// give them a bit of time to terminate
|
||||
// TODO: Instead of just waiting we could periodically check whether the
|
||||
// processes are already gone to shorten the process.
|
||||
snooze(NON_APPS_QUIT_TIMEOUT);
|
||||
|
||||
// iterate through the remaining teams and kill them
|
||||
cookie = 0;
|
||||
while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
|
||||
if (fVitalSystemApps.find(teamInfo.team) == fVitalSystemApps.end()) {
|
||||
PRINT((" killing team %ld\n", teamInfo.team));
|
||||
|
||||
#ifdef __HAIKU__
|
||||
kill_team(teamInfo.team);
|
||||
#else
|
||||
// We don't want to do this when testing under R5, since it
|
||||
// would kill all teams besides our app server and registrar.
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
PRINT(("ShutdownProcess::_QuitNonApps() done\n"));
|
||||
}
|
||||
|
86
src/servers/registrar/ShutdownProcess.h
Normal file
86
src/servers/registrar/ShutdownProcess.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Manages the shutdown process.
|
||||
*/
|
||||
#ifndef SHUTDOWN_PROCESS_H
|
||||
#define SHUTDOWN_PROCESS_H
|
||||
|
||||
#include <hash_set>
|
||||
|
||||
#include <Looper.h>
|
||||
|
||||
#include "AppInfoList.h"
|
||||
#include "EventMaskWatcher.h"
|
||||
|
||||
class EventQueue;
|
||||
class TRoster;
|
||||
|
||||
// Note: EventMaskWatcher is inherited public due to a gcc bug/C++ feature:
|
||||
// Once cast into a Watcher dynamic_cast<>()ing it back into an
|
||||
// EventMaskWatcher fails otherwise.
|
||||
class ShutdownProcess : public BLooper, public EventMaskWatcher {
|
||||
public:
|
||||
ShutdownProcess(TRoster *roster, EventQueue *eventQueue);
|
||||
~ShutdownProcess();
|
||||
|
||||
status_t Init(BMessage *request);
|
||||
|
||||
virtual void MessageReceived(BMessage *message);
|
||||
|
||||
static void SendReply(BMessage *request, status_t error);
|
||||
|
||||
private:
|
||||
void _NegativeQuitRequestReply(thread_id thread);
|
||||
|
||||
void _PrepareShutdownMessage(BMessage &message) const;
|
||||
status_t _ShutDown();
|
||||
|
||||
status_t _PushEvent(uint32 eventType, team_id team, int32 phase);
|
||||
status_t _GetNextEvent(uint32 &eventType, team_id &team, int32 &phase,
|
||||
bool block);
|
||||
|
||||
void _SetPhase(int32 phase);
|
||||
void _ScheduleTimeoutEvent(bigtime_t timeout, team_id team = -1);
|
||||
|
||||
bool _LockAppLists();
|
||||
void _UnlockAppLists();
|
||||
|
||||
static status_t _WorkerEntry(void *data);
|
||||
status_t _Worker();
|
||||
|
||||
void _WorkerDoShutdown();
|
||||
void _QuitUserApps();
|
||||
void _WaitForUserApps();
|
||||
void _KillUserApps();
|
||||
void _QuitSystemApps();
|
||||
void _QuitNonApps();
|
||||
|
||||
private:
|
||||
class TimeoutEvent;
|
||||
class InternalEvent;
|
||||
struct InternalEventList;
|
||||
class QuitRequestReplyHandler;
|
||||
|
||||
friend class QuitRequestReplyHandler;
|
||||
|
||||
BLocker fWorkerLock; // protects fields shared by looper
|
||||
// and worker
|
||||
BMessage *fRequest;
|
||||
TRoster *fRoster;
|
||||
EventQueue *fEventQueue;
|
||||
hash_set<team_id> fVitalSystemApps;
|
||||
AppInfoList fSystemApps;
|
||||
AppInfoList fUserApps;
|
||||
TimeoutEvent *fTimeoutEvent;
|
||||
InternalEventList *fInternalEvents;
|
||||
sem_id fInternalEventSemaphore;
|
||||
QuitRequestReplyHandler *fQuitRequestReplyHandler;
|
||||
thread_id fWorker;
|
||||
int32 fCurrentPhase;
|
||||
status_t fShutdownError;
|
||||
bool fHasGUI;
|
||||
};
|
||||
|
||||
#endif // SHUTDOWN_PROCESS_H
|
@ -1,45 +1,29 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// Copyright (c) 2001-2002, OpenBeOS
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// File Name: TRoster.cpp
|
||||
// Author: Ingo Weinhold (bonefish@users.sf.net)
|
||||
// Description: TRoster is the incarnation of The Roster. It manages the
|
||||
// running applications.
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
* Copyright 2001-2005, Ingo Weinhold, bonefish@users.sf.net.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* TRoster is the incarnation of The Roster. It manages the running
|
||||
* applications.
|
||||
*/
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <Application.h>
|
||||
#include <AppMisc.h>
|
||||
#include <AutoDeleter.h>
|
||||
#include <Autolock.h>
|
||||
#include <File.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <MessagePrivate.h>
|
||||
#include <MessengerPrivate.h>
|
||||
#include <Path.h>
|
||||
#include <ServerProtocol.h>
|
||||
#include <storage_support.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "AppInfoListMessagingTargetSet.h"
|
||||
#include "Debug.h"
|
||||
#include "EventMaskWatcher.h"
|
||||
#include "MessageDeliverer.h"
|
||||
@ -90,6 +74,12 @@ static bool larger_index(const recent_entry *entry1, const recent_entry *entry2)
|
||||
//! The maximal period of time an app may be early pre-registered (60 s).
|
||||
const bigtime_t kMaximalEarlyPreRegistrationPeriod = 60000000LL;
|
||||
|
||||
//! Applications living in this tree are considered "vital system apps".
|
||||
static const char *const kVitalSystemAppPathPrefix
|
||||
= "/boot/beos/system/servers";
|
||||
|
||||
//! Applications living in this tree are considered "system apps".
|
||||
static const char *const kSystemAppPathPrefix = "/boot/beos/system";
|
||||
|
||||
// get_default_roster_settings_file
|
||||
/*! \brief Returns the path to the default roster settings.
|
||||
@ -119,7 +109,8 @@ get_default_roster_settings_file(BPath &path)
|
||||
The object is completely initialized and ready to handle requests.
|
||||
*/
|
||||
TRoster::TRoster()
|
||||
: fRegisteredApps(),
|
||||
: fLock("roster"),
|
||||
fRegisteredApps(),
|
||||
fEarlyPreRegisteredApps(),
|
||||
fIAPRRequests(),
|
||||
fActiveApp(NULL),
|
||||
@ -127,7 +118,8 @@ TRoster::TRoster()
|
||||
fRecentApps(),
|
||||
fRecentDocuments(),
|
||||
fRecentFolders(),
|
||||
fLastToken(0)
|
||||
fLastToken(0),
|
||||
fShuttingDown(false)
|
||||
{
|
||||
_LoadRosterSettings();
|
||||
}
|
||||
@ -148,6 +140,8 @@ TRoster::HandleAddApplication(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
const char *signature;
|
||||
@ -173,9 +167,14 @@ TRoster::HandleAddApplication(BMessage *request)
|
||||
fullReg = false;
|
||||
PRINT(("team: %ld, signature: %s\n", team, signature));
|
||||
PRINT(("full registration: %d\n", fullReg));
|
||||
|
||||
if (fShuttingDown)
|
||||
error = B_SHUTTING_DOWN;
|
||||
|
||||
// check the parameters
|
||||
team_id otherTeam = -1;
|
||||
uint32 launchFlags = flags & B_LAUNCH_MASK;
|
||||
|
||||
// entry_ref
|
||||
if (error == B_OK) {
|
||||
// the entry_ref must be valid
|
||||
@ -194,6 +193,7 @@ PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name));
|
||||
} else
|
||||
SET_ERROR(error, B_ENTRY_NOT_FOUND);
|
||||
}
|
||||
|
||||
// signature
|
||||
if (error == B_OK && signature) {
|
||||
// check exclusive launchers
|
||||
@ -205,6 +205,7 @@ PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name));
|
||||
otherTeam = info->team;
|
||||
}
|
||||
}
|
||||
|
||||
// If no team ID is given, full registration isn't possible.
|
||||
if (error == B_OK) {
|
||||
if (team < 0) {
|
||||
@ -213,6 +214,7 @@ PRINT(("ref: %ld, %lld, %s\n", ref.device, ref.directory, ref.name));
|
||||
} else if (fRegisteredApps.InfoFor(team))
|
||||
SET_ERROR(error, B_REG_ALREADY_REGISTERED);
|
||||
}
|
||||
|
||||
// Add the application info.
|
||||
uint32 token = 0;
|
||||
if (error == B_OK) {
|
||||
@ -245,6 +247,7 @@ PRINT(("added to early pre-regs, token: %lu\n", token));
|
||||
if (error != B_OK && info)
|
||||
delete info;
|
||||
}
|
||||
|
||||
// reply to the request
|
||||
if (error == B_OK) {
|
||||
// add to recent apps if successful
|
||||
@ -279,6 +282,8 @@ TRoster::HandleCompleteRegistration(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -290,13 +295,19 @@ TRoster::HandleCompleteRegistration(BMessage *request)
|
||||
thread = -1;
|
||||
if (request->FindInt32("port", &port) != B_OK)
|
||||
port = -1;
|
||||
|
||||
if (fShuttingDown)
|
||||
error = B_SHUTTING_DOWN;
|
||||
|
||||
// check the parameters
|
||||
// port
|
||||
if (error == B_OK && port < 0)
|
||||
SET_ERROR(error, B_BAD_VALUE);
|
||||
|
||||
// thread
|
||||
if (error == B_OK && thread < 0)
|
||||
SET_ERROR(error, B_BAD_VALUE);
|
||||
|
||||
// team
|
||||
if (error == B_OK) {
|
||||
if (team >= 0) {
|
||||
@ -311,6 +322,7 @@ TRoster::HandleCompleteRegistration(BMessage *request)
|
||||
} else
|
||||
SET_ERROR(error, B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// reply to the request
|
||||
if (error == B_OK) {
|
||||
BMessage reply(B_REG_SUCCESS);
|
||||
@ -333,6 +345,8 @@ TRoster::HandleIsAppPreRegistered(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
entry_ref ref;
|
||||
@ -387,6 +401,8 @@ TRoster::HandleRemovePreRegApp(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
uint32 token;
|
||||
@ -423,6 +439,8 @@ TRoster::HandleRemoveApp(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -459,6 +477,8 @@ TRoster::HandleSetThreadAndTeam(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -535,6 +555,8 @@ TRoster::HandleSetSignature(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -572,6 +594,8 @@ TRoster::HandleGetAppInfo(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -639,6 +663,8 @@ TRoster::HandleGetAppList(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
const char *signature;
|
||||
@ -673,6 +699,8 @@ TRoster::HandleActivateApp(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -707,6 +735,8 @@ TRoster::HandleBroadcast(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
team_id team;
|
||||
@ -721,15 +751,6 @@ TRoster::HandleBroadcast(BMessage *request)
|
||||
error = B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// allocate an error for the message targets
|
||||
BMessenger *targets = NULL;
|
||||
if (error == B_OK) {
|
||||
targets = new(nothrow) BMessenger[fRegisteredApps.CountInfos()];
|
||||
if (!targets)
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
ArrayDeleter<BMessenger> targetsDeleter(targets);
|
||||
|
||||
// reply to the request -- do this first, don't let the inquirer wait
|
||||
if (error == B_OK) {
|
||||
BMessage reply(B_REG_SUCCESS);
|
||||
@ -741,28 +762,33 @@ TRoster::HandleBroadcast(BMessage *request)
|
||||
}
|
||||
|
||||
// broadcast the message
|
||||
team_id registrarTeam = BPrivate::current_team();
|
||||
if (error == B_OK) {
|
||||
// get the list of targets
|
||||
int32 targetCount = 0;
|
||||
for (AppInfoList::Iterator it = fRegisteredApps.It();
|
||||
it.IsValid();
|
||||
++it) {
|
||||
// don't send the message to the requesting team or the registrar
|
||||
if ((*it)->team != team && (*it)->team != registrarTeam) {
|
||||
BMessenger::Private messengerPrivate(targets[targetCount]);
|
||||
messengerPrivate.SetTo((*it)->team, (*it)->port, 0, true);
|
||||
targetCount++;
|
||||
}
|
||||
// the target set (excludes the registrar and the requesting team)
|
||||
class BroadcastMessagingTargetSet
|
||||
: public AppInfoListMessagingTargetSet {
|
||||
public:
|
||||
BroadcastMessagingTargetSet(AppInfoList &list, team_id team)
|
||||
: AppInfoListMessagingTargetSet(list, true),
|
||||
fTeam(team)
|
||||
{
|
||||
}
|
||||
|
||||
if (targetCount > 0) {
|
||||
virtual bool Filter(const RosterAppInfo *info)
|
||||
{
|
||||
return AppInfoListMessagingTargetSet::Filter(info)
|
||||
&& (info->team != fTeam);
|
||||
}
|
||||
|
||||
private:
|
||||
team_id fTeam;
|
||||
} targetSet(fRegisteredApps, team);
|
||||
|
||||
if (targetSet.HasNext()) {
|
||||
// set the reply target
|
||||
BMessage::Private(message).SetReply(replyTarget);
|
||||
|
||||
// send the messages
|
||||
MessageDeliverer::Default()->DeliverMessage(&message, targets,
|
||||
targetCount);
|
||||
MessageDeliverer::Default()->DeliverMessage(&message, targetSet);
|
||||
}
|
||||
}
|
||||
|
||||
@ -778,6 +804,8 @@ TRoster::HandleStartWatching(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
BMessenger target;
|
||||
@ -819,6 +847,8 @@ TRoster::HandleStopWatching(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
// get the parameters
|
||||
BMessenger target;
|
||||
@ -850,7 +880,11 @@ void
|
||||
TRoster::HandleGetRecentDocuments(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
_HandleGetRecentEntries(request);
|
||||
|
||||
FUNCTION_END();
|
||||
}
|
||||
|
||||
@ -862,7 +896,11 @@ void
|
||||
TRoster::HandleGetRecentFolders(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
_HandleGetRecentEntries(request);
|
||||
|
||||
FUNCTION_END();
|
||||
}
|
||||
|
||||
@ -875,6 +913,8 @@ TRoster::HandleGetRecentApps(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!request) {
|
||||
D(PRINT(("WARNING: TRoster::HandleGetRecentApps(NULL) called\n")));
|
||||
return;
|
||||
@ -901,6 +941,8 @@ TRoster::HandleAddToRecentDocuments(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!request) {
|
||||
D(PRINT(("WARNING: TRoster::HandleAddToRecentDocuments(NULL) called\n")));
|
||||
return;
|
||||
@ -930,6 +972,8 @@ TRoster::HandleAddToRecentFolders(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!request) {
|
||||
D(PRINT(("WARNING: TRoster::HandleAddToRecentFolders(NULL) called\n")));
|
||||
return;
|
||||
@ -959,6 +1003,8 @@ TRoster::HandleAddToRecentApps(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!request) {
|
||||
D(PRINT(("WARNING: TRoster::HandleAddToRecentApps(NULL) called\n")));
|
||||
return;
|
||||
@ -981,6 +1027,8 @@ TRoster::HandleLoadRecentLists(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!request) {
|
||||
D(PRINT(("WARNING: TRoster::HandleLoadRecentLists(NULL) called\n")));
|
||||
return;
|
||||
@ -1003,6 +1051,8 @@ TRoster::HandleSaveRecentLists(BMessage *request)
|
||||
{
|
||||
FUNCTION_START();
|
||||
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!request) {
|
||||
D(PRINT(("WARNING: TRoster::HandleSaveRecentLists(NULL) called\n")));
|
||||
return;
|
||||
@ -1026,6 +1076,8 @@ TRoster::HandleSaveRecentLists(BMessage *request)
|
||||
void
|
||||
TRoster::ClearRecentDocuments()
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
fRecentDocuments.Clear();
|
||||
}
|
||||
|
||||
@ -1035,6 +1087,8 @@ TRoster::ClearRecentDocuments()
|
||||
void
|
||||
TRoster::ClearRecentFolders()
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
fRecentFolders.Clear();
|
||||
}
|
||||
|
||||
@ -1044,6 +1098,8 @@ TRoster::ClearRecentFolders()
|
||||
void
|
||||
TRoster::ClearRecentApps()
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
fRecentApps.Clear();
|
||||
}
|
||||
|
||||
@ -1062,14 +1118,21 @@ status_t
|
||||
TRoster::Init()
|
||||
{
|
||||
status_t error = B_OK;
|
||||
|
||||
// check lock initialization
|
||||
if (fLock.Sem() < 0)
|
||||
return fLock.Sem();
|
||||
|
||||
// create the info
|
||||
RosterAppInfo *info = new(nothrow) RosterAppInfo;
|
||||
if (!info)
|
||||
error = B_NO_MEMORY;
|
||||
|
||||
// get the app's ref
|
||||
entry_ref ref;
|
||||
if (error == B_OK)
|
||||
error = get_app_ref(&ref);
|
||||
|
||||
// init and add the info
|
||||
if (error == B_OK) {
|
||||
info->Init(be_app->Thread(), be_app->Team(),
|
||||
@ -1079,9 +1142,11 @@ TRoster::Init()
|
||||
info->registration_time = system_time();
|
||||
error = AddApp(info);
|
||||
}
|
||||
|
||||
// cleanup on error
|
||||
if (error != B_OK && info)
|
||||
delete info;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1093,6 +1158,8 @@ TRoster::Init()
|
||||
status_t
|
||||
TRoster::AddApp(RosterAppInfo *info)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = (info ? B_OK : B_BAD_VALUE);
|
||||
if (info) {
|
||||
if (fRegisteredApps.AddInfo(info))
|
||||
@ -1112,6 +1179,8 @@ TRoster::AddApp(RosterAppInfo *info)
|
||||
void
|
||||
TRoster::RemoveApp(RosterAppInfo *info)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (info) {
|
||||
if (fRegisteredApps.RemoveInfo(info)) {
|
||||
info->state = APP_STATE_UNREGISTERED;
|
||||
@ -1132,6 +1201,8 @@ TRoster::RemoveApp(RosterAppInfo *info)
|
||||
void
|
||||
TRoster::ActivateApp(RosterAppInfo *info)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (info != fActiveApp) {
|
||||
// deactivate the currently active app
|
||||
RosterAppInfo *oldActiveApp = fActiveApp;
|
||||
@ -1154,6 +1225,8 @@ TRoster::ActivateApp(RosterAppInfo *info)
|
||||
void
|
||||
TRoster::CheckSanity()
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
// not early (pre-)registered applications
|
||||
AppInfoList obsoleteApps;
|
||||
for (AppInfoList::Iterator it = fRegisteredApps.It(); it.IsValid(); ++it) {
|
||||
@ -1182,6 +1255,121 @@ TRoster::CheckSanity()
|
||||
}
|
||||
}
|
||||
|
||||
// SetShuttingDown
|
||||
/*! \brief Tells the roster whether a shutdown process is in progess at the
|
||||
moment.
|
||||
|
||||
After this method is called with \a shuttingDown == \c true, no more
|
||||
applications can be created.
|
||||
|
||||
\param shuttingDown \c true, to indicate the start of the shutdown process,
|
||||
\c false to signalling its end.
|
||||
*/
|
||||
void
|
||||
TRoster::SetShuttingDown(bool shuttingDown)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
fShuttingDown = shuttingDown;
|
||||
}
|
||||
|
||||
// GetShutdownApps
|
||||
/*! \brief Returns lists of applications to be asked to quit on shutdown.
|
||||
|
||||
\param userApps List of RosterAppInfos identifying the user applications.
|
||||
Those will be ask to quit first.
|
||||
\param systemApps List of RosterAppInfos identifying the system applications
|
||||
(like Tracker and Deskbar), which will be asked to quit after the
|
||||
user applications are gone.
|
||||
\param vitalSystemApps A set of team_ids identifying teams that must not
|
||||
be terminated (app server and registrar).
|
||||
\return \c B_OK, if everything went fine, another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
TRoster::GetShutdownApps(AppInfoList &userApps, AppInfoList &systemApps,
|
||||
hash_set<team_id> &vitalSystemApps)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
status_t error = B_OK;
|
||||
|
||||
// add ourself to the vital system apps and try to find the app server
|
||||
if (error == B_OK) {
|
||||
// ourself
|
||||
vitalSystemApps.insert(be_app->Team());
|
||||
|
||||
// the app server
|
||||
port_id appServerPort = find_port(SERVER_PORT_NAME);
|
||||
port_info portInfo;
|
||||
if (appServerPort >= 0
|
||||
&& get_port_info(appServerPort, &portInfo) == B_OK) {
|
||||
vitalSystemApps.insert(portInfo.team);
|
||||
}
|
||||
}
|
||||
|
||||
for (AppInfoList::Iterator it(fRegisteredApps.It());
|
||||
RosterAppInfo *info = *it;
|
||||
++it) {
|
||||
if (vitalSystemApps.find(info->team) != vitalSystemApps.end()) {
|
||||
// must be us or the app server
|
||||
} else if (_IsVitalSystemApp(info)) {
|
||||
vitalSystemApps.insert(info->team);
|
||||
} else {
|
||||
RosterAppInfo *clonedInfo = info->Clone();
|
||||
if (clonedInfo) {
|
||||
if (_IsSystemApp(info)) {
|
||||
if (!systemApps.AddInfo(clonedInfo))
|
||||
error = B_NO_MEMORY;
|
||||
} else {
|
||||
if (!userApps.AddInfo(clonedInfo))
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (error != B_OK)
|
||||
delete clonedInfo;
|
||||
} else
|
||||
error = B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (error != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
// clean up on error
|
||||
if (error != B_OK) {
|
||||
userApps.MakeEmpty(true);
|
||||
systemApps.MakeEmpty(true);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
// AddWatcher
|
||||
status_t
|
||||
TRoster::AddWatcher(Watcher *watcher)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (!watcher)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (!fWatchingService.AddWatcher(watcher))
|
||||
return B_NO_MEMORY;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// RemoveWatcher
|
||||
void
|
||||
TRoster::RemoveWatcher(Watcher *watcher)
|
||||
{
|
||||
BAutolock _(fLock);
|
||||
|
||||
if (watcher)
|
||||
fWatchingService.RemoveWatcher(watcher, false);
|
||||
}
|
||||
|
||||
|
||||
// _AppAdded
|
||||
/*! \brief Hook method invoked, when an application has been added.
|
||||
@ -1452,6 +1640,36 @@ TRoster::_HandleGetRecentEntries(BMessage *request)
|
||||
FUNCTION_END();
|
||||
}
|
||||
|
||||
// _IsVitalSystemApp
|
||||
bool
|
||||
TRoster::_IsVitalSystemApp(RosterAppInfo *info) const
|
||||
{
|
||||
BPath path;
|
||||
status_t error = path.SetTo(&info->ref);
|
||||
if (error != B_OK)
|
||||
return false;
|
||||
|
||||
int len = strlen(path.Path());
|
||||
int prefixLen = strlen(kVitalSystemAppPathPrefix);
|
||||
return (len > prefixLen
|
||||
&& strncmp(path.Path(), kVitalSystemAppPathPrefix, prefixLen) == 0);
|
||||
}
|
||||
|
||||
// _IsSystemApp
|
||||
bool
|
||||
TRoster::_IsSystemApp(RosterAppInfo *info) const
|
||||
{
|
||||
BPath path;
|
||||
status_t error = path.SetTo(&info->ref);
|
||||
if (error != B_OK)
|
||||
return false;
|
||||
|
||||
int len = strlen(path.Path());
|
||||
int prefixLen = strlen(kSystemAppPathPrefix);
|
||||
return (len > prefixLen
|
||||
&& strncmp(path.Path(), kSystemAppPathPrefix, prefixLen) == 0);
|
||||
}
|
||||
|
||||
status_t
|
||||
TRoster::_LoadRosterSettings(const char *path)
|
||||
{
|
||||
|
@ -28,8 +28,10 @@
|
||||
#ifndef T_ROSTER_H
|
||||
#define T_ROSTER_H
|
||||
|
||||
#include <hash_set>
|
||||
#include <map>
|
||||
|
||||
#include <Locker.h>
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "AppInfoList.h"
|
||||
@ -48,9 +50,6 @@ struct IAPRRequest {
|
||||
|
||||
typedef map<team_id, IAPRRequest> IAPRRequestMap;
|
||||
|
||||
// For strategic reasons, as TRoster appears in the BMessenger header.
|
||||
namespace BPrivate {
|
||||
|
||||
class TRoster {
|
||||
public:
|
||||
TRoster();
|
||||
@ -90,6 +89,13 @@ public:
|
||||
|
||||
void CheckSanity();
|
||||
|
||||
void SetShuttingDown(bool shuttingDown);
|
||||
status_t GetShutdownApps(AppInfoList &userApps, AppInfoList &systemApps,
|
||||
hash_set<team_id> &vitalSystemApps);
|
||||
|
||||
status_t AddWatcher(Watcher *watcher);
|
||||
void RemoveWatcher(Watcher *watcher);
|
||||
|
||||
private:
|
||||
// hook functions
|
||||
void _AppAdded(RosterAppInfo *info);
|
||||
@ -107,11 +113,15 @@ private:
|
||||
|
||||
void _HandleGetRecentEntries(BMessage *request);
|
||||
|
||||
bool _IsVitalSystemApp(RosterAppInfo *info) const;
|
||||
bool _IsSystemApp(RosterAppInfo *info) const;
|
||||
|
||||
status_t _LoadRosterSettings(const char *path = NULL);
|
||||
status_t _SaveRosterSettings(const char *path = NULL);
|
||||
static const char *kDefaultRosterSettingsFile;
|
||||
|
||||
private:
|
||||
BLocker fLock;
|
||||
AppInfoList fRegisteredApps;
|
||||
AppInfoList fEarlyPreRegisteredApps;
|
||||
IAPRRequestMap fIAPRRequests;
|
||||
@ -121,12 +131,7 @@ private:
|
||||
RecentEntries fRecentDocuments;
|
||||
RecentEntries fRecentFolders;
|
||||
uint32 fLastToken;
|
||||
bool fShuttingDown;
|
||||
};
|
||||
|
||||
}; // namespace BPrivate
|
||||
|
||||
// Only the registrar code uses this header. No need to hide TRoster in the
|
||||
// namespace.
|
||||
using BPrivate::TRoster;
|
||||
|
||||
#endif // T_ROSTER_H
|
||||
|
Loading…
Reference in New Issue
Block a user