launch_daemon: Added basic support for conditions.
* Admittedly not very well thought out, but it should be good enough for now; it doesn't really make sense to initialize jobs that is never run due to failed conditions. * Job, and Target now have a common base class BaseJob that deals with the conditions.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2015, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "BaseJob.h"
|
||||
|
||||
#include "Conditions.h"
|
||||
|
||||
|
||||
BaseJob::BaseJob(const char* name)
|
||||
:
|
||||
BJob(name),
|
||||
fCondition(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
BaseJob::Name() const
|
||||
{
|
||||
return Title().String();
|
||||
}
|
||||
|
||||
|
||||
const ::Condition*
|
||||
BaseJob::Condition() const
|
||||
{
|
||||
return fCondition;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BaseJob::SetCondition(const ::Condition* condition)
|
||||
{
|
||||
fCondition = condition;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BaseJob::CheckCondition(ConditionContext& context) const
|
||||
{
|
||||
if (fCondition != NULL)
|
||||
return fCondition->Test(context);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2015, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef BASE_JOB_H
|
||||
#define BASE_JOB_H
|
||||
|
||||
|
||||
#include <Job.h>
|
||||
|
||||
|
||||
using namespace BSupportKit;
|
||||
|
||||
class Condition;
|
||||
class ConditionContext;
|
||||
|
||||
|
||||
class BaseJob : public BJob {
|
||||
public:
|
||||
BaseJob(const char* name);
|
||||
|
||||
const char* Name() const;
|
||||
|
||||
const ::Condition* Condition() const;
|
||||
void SetCondition(const ::Condition* condition);
|
||||
bool CheckCondition(ConditionContext& context) const;
|
||||
|
||||
protected:
|
||||
const ::Condition* fCondition;
|
||||
};
|
||||
|
||||
|
||||
#endif // BASE_JOB_H
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright 2015, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "Conditions.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <ObjectList.h>
|
||||
#include <Message.h>
|
||||
#include <StringList.h>
|
||||
|
||||
#include <DiskDevice.h>
|
||||
#include <DiskDeviceRoster.h>
|
||||
#include <Volume.h>
|
||||
|
||||
|
||||
class ConditionContainer : public Condition {
|
||||
protected:
|
||||
ConditionContainer(const BMessage& args);
|
||||
|
||||
void AddCondition(Condition* condition);
|
||||
|
||||
protected:
|
||||
BObjectList<Condition> fConditions;
|
||||
};
|
||||
|
||||
|
||||
class AndCondition : public ConditionContainer {
|
||||
public:
|
||||
AndCondition(const BMessage& args);
|
||||
|
||||
virtual bool Test(ConditionContext& context) const;
|
||||
};
|
||||
|
||||
|
||||
class OrCondition : public ConditionContainer {
|
||||
public:
|
||||
OrCondition(const BMessage& args);
|
||||
|
||||
virtual bool Test(ConditionContext& context) const;
|
||||
};
|
||||
|
||||
|
||||
class NotCondition : public ConditionContainer {
|
||||
public:
|
||||
NotCondition(const BMessage& args);
|
||||
|
||||
virtual bool Test(ConditionContext& context) const;
|
||||
};
|
||||
|
||||
|
||||
class SafeModeCondition : public Condition {
|
||||
public:
|
||||
virtual bool Test(ConditionContext& context) const;
|
||||
};
|
||||
|
||||
|
||||
class ReadOnlyCondition : public Condition {
|
||||
public:
|
||||
ReadOnlyCondition(const BMessage& args);
|
||||
|
||||
virtual bool Test(ConditionContext& context) const;
|
||||
|
||||
private:
|
||||
dev_t fDevice;
|
||||
};
|
||||
|
||||
|
||||
class FileExistsCondition : public Condition {
|
||||
public:
|
||||
FileExistsCondition(const BMessage& args);
|
||||
|
||||
virtual bool Test(ConditionContext& context) const;
|
||||
|
||||
private:
|
||||
BStringList fPaths;
|
||||
};
|
||||
|
||||
|
||||
static Condition*
|
||||
create_condition(const char* name, const BMessage& args)
|
||||
{
|
||||
if (strcmp(name, "and") == 0)
|
||||
return new AndCondition(args);
|
||||
if (strcmp(name, "or") == 0)
|
||||
return new OrCondition(args);
|
||||
if (strcmp(name, "not") == 0)
|
||||
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);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Condition::Condition()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Condition::~Condition()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
ConditionContainer::ConditionContainer(const BMessage& args)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ConditionContainer::AddCondition(Condition* condition)
|
||||
{
|
||||
if (condition != NULL)
|
||||
fConditions.AddItem(condition);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - and
|
||||
|
||||
|
||||
AndCondition::AndCondition(const BMessage& args)
|
||||
:
|
||||
ConditionContainer(args)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// #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;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - or
|
||||
|
||||
|
||||
NotCondition::NotCondition(const BMessage& args)
|
||||
:
|
||||
ConditionContainer(args)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - safemode
|
||||
|
||||
|
||||
bool
|
||||
SafeModeCondition::Test(ConditionContext& context) const
|
||||
{
|
||||
return context.IsSafeMode();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - read_only
|
||||
|
||||
|
||||
ReadOnlyCondition::ReadOnlyCondition(const BMessage& args)
|
||||
{
|
||||
fDevice = dev_for_path(args.GetString("args", "/boot"));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ReadOnlyCondition::Test(ConditionContext& context) const
|
||||
{
|
||||
BVolume volume;
|
||||
status_t status = volume.SetTo(fDevice);
|
||||
if (status != B_OK) {
|
||||
fprintf(stderr, "Failed to get BVolume for device %" B_PRIdDEV
|
||||
": %s\n", fDevice, strerror(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
BDiskDeviceRoster roster;
|
||||
BDiskDevice diskDevice;
|
||||
BPartition* partition;
|
||||
status = roster.FindPartitionByVolume(volume, &diskDevice, &partition);
|
||||
if (status != B_OK) {
|
||||
fprintf(stderr, "Failed to get partition for device %" B_PRIdDEV
|
||||
": %s\n", fDevice, strerror(status));
|
||||
return false;
|
||||
}
|
||||
|
||||
return partition->IsReadOnly();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - file_exists
|
||||
|
||||
|
||||
FileExistsCondition::FileExistsCondition(const BMessage& args)
|
||||
{
|
||||
for (int32 index = 0;
|
||||
const char* path = args.GetString("args", index, NULL); index++) {
|
||||
fPaths.Add(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;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
/*static*/ Condition*
|
||||
Conditions::FromMessage(const BMessage& message)
|
||||
{
|
||||
return create_condition("and", message);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2015, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CONDITIONS_H
|
||||
#define CONDITIONS_H
|
||||
|
||||
|
||||
class BMessage;
|
||||
|
||||
|
||||
class ConditionContext {
|
||||
public:
|
||||
virtual bool IsSafeMode() const = 0;
|
||||
};
|
||||
|
||||
|
||||
class Condition {
|
||||
public:
|
||||
Condition();
|
||||
virtual ~Condition();
|
||||
|
||||
virtual bool Test(ConditionContext& context) const = 0;
|
||||
};
|
||||
|
||||
|
||||
class Conditions {
|
||||
public:
|
||||
static Condition* FromMessage(const BMessage& message);
|
||||
};
|
||||
|
||||
|
||||
#endif // CONDITIONS_H
|
||||
@@ -8,6 +8,9 @@ UseHeaders [ FDirName $(HAIKU_TOP) src bin multiuser ] ;
|
||||
Server launch_daemon
|
||||
:
|
||||
LaunchDaemon.cpp
|
||||
|
||||
BaseJob.cpp
|
||||
Conditions.cpp
|
||||
Job.cpp
|
||||
SettingsParser.cpp
|
||||
Target.cpp
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
Job::Job(const char* name)
|
||||
:
|
||||
BJob(name),
|
||||
BaseJob(name),
|
||||
fEnabled(true),
|
||||
fService(false),
|
||||
fCreateDefaultPort(false),
|
||||
@@ -30,7 +30,7 @@ Job::Job(const char* name)
|
||||
|
||||
Job::Job(const Job& other)
|
||||
:
|
||||
BJob(other.Name()),
|
||||
BaseJob(other.Name()),
|
||||
fEnabled(other.IsEnabled()),
|
||||
fService(other.IsService()),
|
||||
fCreateDefaultPort(other.CreateDefaultPort()),
|
||||
@@ -39,6 +39,8 @@ Job::Job(const Job& other)
|
||||
fTeam(-1),
|
||||
fTarget(other.Target())
|
||||
{
|
||||
fCondition = other.fCondition;
|
||||
|
||||
for (int32 i = 0; i < other.Arguments().CountStrings(); i++)
|
||||
AddArgument(other.Arguments().StringAt(i));
|
||||
|
||||
@@ -63,13 +65,6 @@ Job::~Job()
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Job::Name() const
|
||||
{
|
||||
return Title().String();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Job::IsEnabled() const
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define JOB_H
|
||||
|
||||
|
||||
#include <Job.h>
|
||||
#include "BaseJob.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -25,14 +25,12 @@ class Target;
|
||||
typedef std::map<BString, BMessage> PortMap;
|
||||
|
||||
|
||||
class Job : public BJob {
|
||||
class Job : public BaseJob {
|
||||
public:
|
||||
Job(const char* name);
|
||||
Job(const Job& other);
|
||||
virtual ~Job();
|
||||
|
||||
const char* Name() const;
|
||||
|
||||
bool IsEnabled() const;
|
||||
void SetEnabled(bool enable);
|
||||
|
||||
@@ -89,6 +87,7 @@ private:
|
||||
status_t fInitStatus;
|
||||
team_id fTeam;
|
||||
::Target* fTarget;
|
||||
::Condition* fCondition;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "multiuser_utils.h"
|
||||
|
||||
#include "Conditions.h"
|
||||
#include "InitRealTimeClockJob.h"
|
||||
#include "InitSharedMemoryDirectoryJob.h"
|
||||
#include "InitTemporaryDirectoryJob.h"
|
||||
@@ -67,7 +68,7 @@ typedef std::map<uid_t, Session*> SessionMap;
|
||||
typedef std::map<BString, Target*> TargetMap;
|
||||
|
||||
|
||||
class LaunchDaemon : public BServer, public Finder {
|
||||
class LaunchDaemon : public BServer, public Finder, public ConditionContext {
|
||||
public:
|
||||
LaunchDaemon(bool userMode, status_t& error);
|
||||
virtual ~LaunchDaemon();
|
||||
@@ -76,6 +77,8 @@ public:
|
||||
virtual Target* FindTarget(const char* name) const;
|
||||
Session* FindSession(uid_t user) const;
|
||||
|
||||
virtual bool IsSafeMode() const;
|
||||
|
||||
virtual void ReadyToRun();
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
@@ -104,8 +107,6 @@ private:
|
||||
void _InitSystem();
|
||||
void _AddInitJob(BJob* job);
|
||||
|
||||
bool _IsSafeMode() const;
|
||||
|
||||
private:
|
||||
JobMap fJobs;
|
||||
TargetMap fTargets;
|
||||
@@ -205,6 +206,13 @@ LaunchDaemon::FindSession(uid_t user) const
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LaunchDaemon::IsSafeMode() const
|
||||
{
|
||||
return fSafeMode;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LaunchDaemon::ReadyToRun()
|
||||
{
|
||||
@@ -250,6 +258,12 @@ LaunchDaemon::MessageReceived(BMessage* message)
|
||||
break;
|
||||
}
|
||||
reply.what = B_NAME_NOT_FOUND;
|
||||
} else if (!job->IsLaunched() && !job->CheckCondition(*this)) {
|
||||
// The job exists, but cannot be started yet, as its
|
||||
// conditions are not met; don't make it available yet
|
||||
// TODO: we may not want to initialize jobs with conditions
|
||||
// that aren't met yet
|
||||
reply.what = B_NO_INIT;
|
||||
} else {
|
||||
// If the job has not been launched yet, we'll pass on our
|
||||
// team here. The rationale behind this is that this team
|
||||
@@ -547,7 +561,7 @@ LaunchDaemon::_InitJobs()
|
||||
JobMap::iterator remove = iterator++;
|
||||
|
||||
status_t status = B_NO_INIT;
|
||||
if (job->IsEnabled() && (!_IsSafeMode() || job->LaunchInSafeMode())) {
|
||||
if (job->IsEnabled() && (!IsSafeMode() || job->LaunchInSafeMode())) {
|
||||
std::set<BString> dependencies;
|
||||
status = job->Init(*this, dependencies);
|
||||
}
|
||||
@@ -570,6 +584,9 @@ LaunchDaemon::_InitJobs()
|
||||
void
|
||||
LaunchDaemon::_LaunchJobs(Target* target)
|
||||
{
|
||||
if (target != NULL && !target->CheckCondition(*this))
|
||||
return;
|
||||
|
||||
for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
|
||||
iterator++) {
|
||||
Job* job = iterator->second;
|
||||
@@ -582,7 +599,7 @@ LaunchDaemon::_LaunchJobs(Target* target)
|
||||
void
|
||||
LaunchDaemon::_AddLaunchJob(Job* job)
|
||||
{
|
||||
if (!job->IsLaunched())
|
||||
if (!job->IsLaunched() && job->CheckCondition(*this))
|
||||
fJobQueue.AddJob(job);
|
||||
}
|
||||
|
||||
@@ -677,7 +694,7 @@ LaunchDaemon::_SetupEnvironment()
|
||||
{
|
||||
// Determine safemode kernel option
|
||||
BString safemode = "SAFEMODE=";
|
||||
safemode << (_IsSafeMode() ? "yes" : "no");
|
||||
safemode << (IsSafeMode() ? "yes" : "no");
|
||||
|
||||
putenv(safemode.String());
|
||||
}
|
||||
@@ -704,13 +721,6 @@ LaunchDaemon::_AddInitJob(BJob* job)
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LaunchDaemon::_IsSafeMode() const
|
||||
{
|
||||
return fSafeMode;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
|
||||
@@ -9,18 +9,11 @@
|
||||
|
||||
Target::Target(const char* name)
|
||||
:
|
||||
BJob(name)
|
||||
BaseJob(name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Target::Name() const
|
||||
{
|
||||
return Title().String();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Target::AddData(const char* name, BMessage& data)
|
||||
{
|
||||
|
||||
@@ -6,19 +6,18 @@
|
||||
#define TARGET_H
|
||||
|
||||
|
||||
#include <Job.h>
|
||||
#include "BaseJob.h"
|
||||
|
||||
#include <Message.h>
|
||||
|
||||
|
||||
using namespace BSupportKit;
|
||||
|
||||
|
||||
class Target : public BJob {
|
||||
class Target : public BaseJob {
|
||||
public:
|
||||
Target(const char* name);
|
||||
|
||||
const char* Name() const;
|
||||
|
||||
status_t AddData(const char* name, BMessage& data);
|
||||
const BMessage& Data() const
|
||||
{ return fData; }
|
||||
|
||||
@@ -140,7 +140,7 @@ SettingsParserTest::TestConditionsMultiLineFlatNotWithArgs()
|
||||
{
|
||||
BMessage message;
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, _ParseCondition("if {\n"
|
||||
"\tnot file_exists one\n"
|
||||
"\tnot file_exists one two\n"
|
||||
"}\n", message));
|
||||
|
||||
BMessage subMessage;
|
||||
@@ -151,7 +151,9 @@ SettingsParserTest::TestConditionsMultiLineFlatNotWithArgs()
|
||||
CPPUNIT_ASSERT_EQUAL(B_OK, subMessage.FindMessage("file_exists", &args));
|
||||
CPPUNIT_ASSERT_EQUAL(BString("one"),
|
||||
BString(args.GetString("args", 0, "-")));
|
||||
CPPUNIT_ASSERT_EQUAL(1, args.CountNames(B_ANY_TYPE));
|
||||
CPPUNIT_ASSERT_EQUAL(BString("two"),
|
||||
BString(args.GetString("args", 1, "-")));
|
||||
CPPUNIT_ASSERT_EQUAL(2, args.CountNames(B_ANY_TYPE));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user