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:
Axel Dörfler
2015-07-22 20:42:41 +02:00
parent f7cf381a14
commit 1e9c987102
11 changed files with 445 additions and 40 deletions
+48
View File
@@ -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;
}
+33
View File
@@ -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
+290
View File
@@ -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);
}
+33
View File
@@ -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
+3
View File
@@ -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
+4 -9
View File
@@ -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
{
+3 -4
View File
@@ -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;
};
+23 -13
View File
@@ -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 -
+1 -8
View File
@@ -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)
{
+3 -4
View File
@@ -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));
}