haiku/src/servers/launch/Conditions.cpp

591 lines
11 KiB
C++

/*
* Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include "Conditions.h"
#include <stdio.h>
#include <Entry.h>
#include <File.h>
#include <ObjectList.h>
#include <Message.h>
#include <Path.h>
#include <StringList.h>
#include "NetworkWatcher.h"
#include "Utility.h"
class ConditionContainer : public Condition {
protected:
ConditionContainer(const BMessage& args);
ConditionContainer();
public:
void AddCondition(Condition* condition);
virtual bool IsConstant(ConditionContext& context) const;
protected:
void AddConditionsToString(BString& string) const;
protected:
BObjectList<Condition> fConditions;
};
class AndCondition : public ConditionContainer {
public:
AndCondition(const BMessage& args);
AndCondition();
virtual bool Test(ConditionContext& context) const;
virtual BString ToString() const;
};
class OrCondition : public ConditionContainer {
public:
OrCondition(const BMessage& args);
virtual bool Test(ConditionContext& context) const;
virtual bool IsConstant(ConditionContext& context) const;
virtual BString ToString() const;
};
class NotCondition : public ConditionContainer {
public:
NotCondition(const BMessage& args);
NotCondition();
virtual bool Test(ConditionContext& context) const;
virtual BString ToString() const;
};
class SafeModeCondition : public Condition {
public:
virtual bool Test(ConditionContext& context) const;
virtual bool IsConstant(ConditionContext& context) const;
virtual BString ToString() const;
};
class ReadOnlyCondition : public Condition {
public:
ReadOnlyCondition(const BMessage& args);
virtual bool Test(ConditionContext& context) const;
virtual bool IsConstant(ConditionContext& context) const;
virtual BString ToString() const;
private:
BString fPath;
mutable bool fIsReadOnly;
mutable bool fTestPerformed;
};
class FileExistsCondition : public Condition {
public:
FileExistsCondition(const BMessage& args);
virtual bool Test(ConditionContext& context) const;
virtual BString ToString() const;
private:
BStringList fPaths;
};
class NetworkAvailableCondition : public Condition {
public:
virtual bool Test(ConditionContext& context) const;
virtual bool IsConstant(ConditionContext& context) const;
virtual BString ToString() const;
};
class SettingCondition : public Condition {
public:
SettingCondition(const BMessage& args);
virtual bool Test(ConditionContext& context) const;
virtual BString ToString() const;
private:
BPath fPath;
BString fField;
BString fValue;
};
static Condition*
create_condition(const char* name, const BMessage& args)
{
if (strcmp(name, "and") == 0 && !args.IsEmpty())
return new AndCondition(args);
if (strcmp(name, "or") == 0 && !args.IsEmpty())
return new OrCondition(args);
if (strcmp(name, "not") == 0 && !args.IsEmpty())
return new NotCondition(args);
if (strcmp(name, "safemode") == 0)
return new SafeModeCondition();
if (strcmp(name, "read_only") == 0)
return new ReadOnlyCondition(args);
if (strcmp(name, "file_exists") == 0)
return new FileExistsCondition(args);
if (strcmp(name, "network_available") == 0)
return new NetworkAvailableCondition();
if (strcmp(name, "setting") == 0)
return new SettingCondition(args);
return NULL;
}
// #pragma mark -
Condition::Condition()
{
}
Condition::~Condition()
{
}
bool
Condition::IsConstant(ConditionContext& context) const
{
return false;
}
// #pragma mark -
ConditionContainer::ConditionContainer(const BMessage& args)
:
fConditions(10, true)
{
char* name;
type_code type;
int32 count;
for (int32 index = 0; args.GetInfo(B_MESSAGE_TYPE, index, &name, &type,
&count) == B_OK; index++) {
BMessage message;
for (int32 messageIndex = 0; args.FindMessage(name, messageIndex,
&message) == B_OK; messageIndex++) {
AddCondition(create_condition(name, message));
}
}
}
ConditionContainer::ConditionContainer()
:
fConditions(10, true)
{
}
void
ConditionContainer::AddCondition(Condition* condition)
{
if (condition != NULL)
fConditions.AddItem(condition);
}
/*! A single constant failing condition makes this constant, too, otherwise,
a single non-constant condition makes this non-constant as well.
*/
bool
ConditionContainer::IsConstant(ConditionContext& context) const
{
bool fixed = true;
for (int32 index = 0; index < fConditions.CountItems(); index++) {
const Condition* condition = fConditions.ItemAt(index);
if (condition->IsConstant(context)) {
if (!condition->Test(context))
return true;
} else
fixed = false;
}
return fixed;
}
void
ConditionContainer::AddConditionsToString(BString& string) const
{
string += "[";
for (int32 index = 0; index < fConditions.CountItems(); index++) {
if (index != 0)
string += ", ";
string += fConditions.ItemAt(index)->ToString();
}
string += "]";
}
// #pragma mark - and
AndCondition::AndCondition(const BMessage& args)
:
ConditionContainer(args)
{
}
AndCondition::AndCondition()
{
}
bool
AndCondition::Test(ConditionContext& context) const
{
for (int32 index = 0; index < fConditions.CountItems(); index++) {
Condition* condition = fConditions.ItemAt(index);
if (!condition->Test(context))
return false;
}
return true;
}
BString
AndCondition::ToString() const
{
BString string = "and ";
ConditionContainer::AddConditionsToString(string);
return string;
}
// #pragma mark - or
OrCondition::OrCondition(const BMessage& args)
:
ConditionContainer(args)
{
}
bool
OrCondition::Test(ConditionContext& context) const
{
if (fConditions.IsEmpty())
return true;
for (int32 index = 0; index < fConditions.CountItems(); index++) {
Condition* condition = fConditions.ItemAt(index);
if (condition->Test(context))
return true;
}
return false;
}
/*! If there is a single succeeding constant condition, this is constant, too.
Otherwise, it is non-constant if there is a single non-constant condition.
*/
bool
OrCondition::IsConstant(ConditionContext& context) const
{
bool fixed = true;
for (int32 index = 0; index < fConditions.CountItems(); index++) {
const Condition* condition = fConditions.ItemAt(index);
if (condition->IsConstant(context)) {
if (condition->Test(context))
return true;
} else
fixed = false;
}
return fixed;
}
BString
OrCondition::ToString() const
{
BString string = "or ";
ConditionContainer::AddConditionsToString(string);
return string;
}
// #pragma mark - or
NotCondition::NotCondition(const BMessage& args)
:
ConditionContainer(args)
{
}
NotCondition::NotCondition()
{
}
bool
NotCondition::Test(ConditionContext& context) const
{
for (int32 index = 0; index < fConditions.CountItems(); index++) {
Condition* condition = fConditions.ItemAt(index);
if (condition->Test(context))
return false;
}
return true;
}
BString
NotCondition::ToString() const
{
BString string = "not ";
ConditionContainer::AddConditionsToString(string);
return string;
}
// #pragma mark - safemode
bool
SafeModeCondition::Test(ConditionContext& context) const
{
return context.IsSafeMode();
}
bool
SafeModeCondition::IsConstant(ConditionContext& context) const
{
return true;
}
BString
SafeModeCondition::ToString() const
{
return "safemode";
}
// #pragma mark - read_only
ReadOnlyCondition::ReadOnlyCondition(const BMessage& args)
:
fPath(args.GetString("args")),
fIsReadOnly(false),
fTestPerformed(false)
{
}
bool
ReadOnlyCondition::Test(ConditionContext& context) const
{
if (fTestPerformed)
return fIsReadOnly;
if (fPath.IsEmpty() || fPath == "/boot")
fIsReadOnly = context.BootVolumeIsReadOnly();
else
fIsReadOnly = Utility::IsReadOnlyVolume(fPath);
fTestPerformed = true;
return fIsReadOnly;
}
bool
ReadOnlyCondition::IsConstant(ConditionContext& context) const
{
return true;
}
BString
ReadOnlyCondition::ToString() const
{
BString string = "readonly ";
string << fPath;
return string;
}
// #pragma mark - file_exists
FileExistsCondition::FileExistsCondition(const BMessage& args)
{
for (int32 index = 0;
const char* path = args.GetString("args", index, NULL); index++) {
fPaths.Add(Utility::TranslatePath(path));
}
}
bool
FileExistsCondition::Test(ConditionContext& context) const
{
for (int32 index = 0; index < fPaths.CountStrings(); index++) {
BEntry entry;
if (entry.SetTo(fPaths.StringAt(index)) != B_OK
|| !entry.Exists())
return false;
}
return true;
}
BString
FileExistsCondition::ToString() const
{
BString string = "file_exists [";
for (int32 index = 0; index < fPaths.CountStrings(); index++) {
if (index != 0)
string << ", ";
string << fPaths.StringAt(index);
}
string += "]";
return string;
}
// #pragma mark - network_available
bool
NetworkAvailableCondition::Test(ConditionContext& context) const
{
return NetworkWatcher::NetworkAvailable(false);
}
bool
NetworkAvailableCondition::IsConstant(ConditionContext& context) const
{
return false;
}
BString
NetworkAvailableCondition::ToString() const
{
return "network_available";
}
// #pragma mark - setting
SettingCondition::SettingCondition(const BMessage& args)
{
fPath.SetTo(Utility::TranslatePath(args.GetString("args", 0, NULL)));
fField = args.GetString("args", 1, NULL);
fValue = args.GetString("args", 2, NULL);
}
bool
SettingCondition::Test(ConditionContext& context) const
{
BFile file(fPath.Path(), B_READ_ONLY);
if (file.InitCheck() != B_OK)
return false;
BMessage settings;
if (settings.Unflatten(&file) == B_OK) {
type_code type;
int32 count;
if (settings.GetInfo(fField, &type, &count) == B_OK) {
switch (type) {
case B_BOOL_TYPE:
{
bool value = settings.GetBool(fField);
bool expect = fValue.IsEmpty();
if (!expect) {
expect = fValue == "true" || fValue == "yes"
|| fValue == "on" || fValue == "1";
}
return value == expect;
}
case B_STRING_TYPE:
{
BString value = settings.GetString(fField);
if (fValue.IsEmpty() && !value.IsEmpty())
return true;
return fValue == value;
}
}
}
}
// TODO: check for driver settings, too?
return false;
}
BString
SettingCondition::ToString() const
{
BString string = "setting file ";
string << fPath.Path() << ", field " << fField;
if (!fValue.IsEmpty())
string << ", value " << fValue;
return string;
}
// #pragma mark -
/*static*/ Condition*
Conditions::FromMessage(const BMessage& message)
{
return create_condition("and", message);
}
/*static*/ Condition*
Conditions::AddNotSafeMode(Condition* condition)
{
AndCondition* andCondition = dynamic_cast<AndCondition*>(condition);
if (andCondition == NULL)
andCondition = new AndCondition();
if (andCondition != condition && condition != NULL)
andCondition->AddCondition(condition);
NotCondition* notCondition = new NotCondition();
notCondition->AddCondition(new SafeModeCondition());
andCondition->AddCondition(notCondition);
return andCondition;
}