From e2f83cbd6b2a52abb62fef4407c2a97023055648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 6 Nov 2015 22:40:04 +0100 Subject: [PATCH] launch_daemon: Monitor teams, and restart services. * Services that end are now automatically restarted. * However, this is very basic at the moment, there is no throttling, and no support for shutting down the system. --- src/servers/launch/Job.cpp | 43 +++++++++++++++++-- src/servers/launch/Job.h | 14 +++++++ src/servers/launch/LaunchDaemon.cpp | 65 +++++++++++++++++++++++++++-- 3 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/servers/launch/Job.cpp b/src/servers/launch/Job.cpp index d7858116be..5cdeeab428 100644 --- a/src/servers/launch/Job.cpp +++ b/src/servers/launch/Job.cpp @@ -81,6 +81,20 @@ Job::~Job() } +::TeamRegistrator* +Job::TeamRegistrator() const +{ + return fTeamRegistrator; +} + + +void +Job::SetTeamRegistrator(::TeamRegistrator* registrator) +{ + fTeamRegistrator = registrator; +} + + bool Job::IsEnabled() const { @@ -349,8 +363,24 @@ Job::IsLaunched() const bool Job::IsRunning() const { - // TODO: monitor team status; should jobs be allowed to run multiple times? - return State() == B_JOB_STATE_SUCCEEDED && IsLaunched() && IsService(); + return fTeam >= 0; +} + + +void +Job::TeamDeleted() +{ + fTeam = -1; + if (IsService()) + SetState(B_JOB_STATE_WAITING_TO_RUN); +} + + +bool +Job::CanBeLaunched() const +{ + // Services cannot be launched while they are running + return !IsLaunching() && (!IsService() || !IsRunning()); } @@ -384,7 +414,7 @@ Job::Run() { status_t status = BJob::Run(); - // TODO: monitor team, don't just do this + // Jobs can be relaunched at any time if (!IsService()) SetState(B_JOB_STATE_WAITING_TO_RUN); @@ -396,8 +426,10 @@ status_t Job::Execute() { status_t status = B_OK; - if (!IsLaunched() || !IsService()) + if (!IsRunning() || !IsService()) status = Launch(); + else + debug_printf("Ignore launching %s\n", Name()); fLaunching = false; return status; @@ -460,6 +492,9 @@ Job::_SetLaunchStatus(status_t launchStatus) fLaunchStatus = launchStatus != B_NO_INIT ? launchStatus : B_ERROR; launchLocker.Unlock(); + if (fTeamRegistrator != NULL) + fTeamRegistrator->RegisterTeam(this); + _SendPendingLaunchDataReplies(); } diff --git a/src/servers/launch/Job.h b/src/servers/launch/Job.h index 73d1ce19b2..b18dbd1099 100644 --- a/src/servers/launch/Job.h +++ b/src/servers/launch/Job.h @@ -22,6 +22,7 @@ using namespace BSupportKit; class BMessage; class Finder; +class Job; class Target; struct entry_ref; @@ -30,12 +31,22 @@ struct entry_ref; typedef std::map PortMap; +class TeamRegistrator { +public: + virtual void RegisterTeam(Job* job) = 0; +}; + + class Job : public BaseJob { public: Job(const char* name); Job(const Job& other); virtual ~Job(); + ::TeamRegistrator* TeamRegistrator() const; + void SetTeamRegistrator( + ::TeamRegistrator* registrator); + bool IsEnabled() const; void SetEnabled(bool enable); @@ -72,6 +83,8 @@ public: status_t Launch(); bool IsLaunched() const; bool IsRunning() const; + void TeamDeleted(); + bool CanBeLaunched() const; bool IsLaunching() const; void SetLaunching(bool launching); @@ -117,6 +130,7 @@ private: ::Condition* fCondition; BObjectList fPendingLaunchDataReplies; + ::TeamRegistrator* fTeamRegistrator; }; diff --git a/src/servers/launch/LaunchDaemon.cpp b/src/servers/launch/LaunchDaemon.cpp index 5caf90fd5c..3ac3c62713 100644 --- a/src/servers/launch/LaunchDaemon.cpp +++ b/src/servers/launch/LaunchDaemon.cpp @@ -27,8 +27,11 @@ #include #include #include +#include +#include #include #include +#include #include "multiuser_utils.h" @@ -116,10 +119,11 @@ typedef std::map JobMap; typedef std::map SessionMap; typedef std::map TargetMap; typedef std::map EventMap; +typedef std::map TeamMap; class LaunchDaemon : public BServer, public Finder, public ConditionContext, - public EventRegistrator { + public EventRegistrator, public TeamRegistrator { public: LaunchDaemon(bool userMode, const EventMap& events, status_t& error); @@ -140,6 +144,9 @@ public: virtual void UnregisterExternalEvent(Event* event, const char* name); + // TeamRegistrator + virtual void RegisterTeam(Job* job); + virtual void ReadyToRun(); virtual void MessageReceived(BMessage* message); @@ -208,6 +215,8 @@ private: SessionMap fSessions; MainWorker* fMainWorker; Target* fInitTarget; + TeamMap fTeams; + mutex fTeamsLock; bool fSafeMode; bool fReadOnlyBootVolume; bool fUserMode; @@ -305,6 +314,8 @@ LaunchDaemon::LaunchDaemon(bool userMode, const EventMap& events, fInitTarget(userMode ? NULL : new Target("init")), fUserMode(userMode) { + mutex_init(&fTeamsLock, "teams lock"); + fMainWorker = new MainWorker(fJobQueue); fMainWorker->Init(); @@ -391,6 +402,14 @@ LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name) } +void +LaunchDaemon::RegisterTeam(Job* job) +{ + MutexLocker locker(fTeamsLock); + fTeams.insert(std::make_pair(job->Team(), job)); +} + + void LaunchDaemon::ReadyToRun() { @@ -429,6 +448,12 @@ LaunchDaemon::ReadyToRun() fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); _ReadPaths(paths); + BMessenger target(this); + BMessenger::Private messengerPrivate(target); + port_id port = messengerPrivate.Port(); + int32 token = messengerPrivate.Token(); + __start_watching_system(-1, B_WATCH_SYSTEM_TEAM_DELETION, port, token); + _InitJobs(NULL); _LaunchJobs(NULL); @@ -445,6 +470,30 @@ void LaunchDaemon::MessageReceived(BMessage* message) { switch (message->what) { + case B_SYSTEM_OBJECT_UPDATE: + { + int32 opcode = message->GetInt32("opcode", 0); + team_id team = (team_id)message->GetInt32("team", -1); + if (opcode != B_TEAM_DELETED || team < 0) + break; + + MutexLocker locker(fTeamsLock); + + TeamMap::iterator found = fTeams.find(team); + if (found != fTeams.end()) { + Job* job = found->second; + TRACE("Job %s ended!\n", job->Name()); + job->TeamDeleted(); + + if (job->IsService()) { + // TODO: take restart throttle into account + // TODO: don't restart on shutdown + _LaunchJob(job); + } + } + break; + } + case B_GET_LAUNCH_DATA: _HandleGetLaunchData(message); break; @@ -1112,6 +1161,7 @@ LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) if (job == NULL) return; + job->SetTeamRegistrator(this); job->SetService(service); job->SetCreateDefaultPort(service); job->SetTarget(target); @@ -1232,7 +1282,7 @@ LaunchDaemon::_LaunchJobs(Target* target, bool forceNow) void LaunchDaemon::_LaunchJob(Job* job, uint32 options) { - if (job == NULL || job->IsLaunching() || job->IsRunning() + if (job == NULL || !job->CanBeLaunched() || ((options & FORCE_NOW) == 0 && (!job->EventHasTriggered() || !job->CheckCondition(*this) || ((options & TRIGGER_DEMAND) != 0 @@ -1256,7 +1306,12 @@ LaunchDaemon::_LaunchJob(Job* job, uint32 options) job->Event()->ResetTrigger(); job->SetLaunching(true); - fJobQueue.AddJob(job); + + status_t status = fJobQueue.AddJob(job); + if (status != B_OK) { + debug_printf("Adding job %s to queue failed: %s\n", job->Name(), + strerror(status)); + } } @@ -1532,7 +1587,11 @@ LaunchDaemon::_AddInitJob(BJob* job) static void open_stdio(int targetFD, int openMode) { +#ifdef DEBUG + int fd = open("/dev/dprintf", openMode); +#else int fd = open("/dev/null", openMode); +#endif if (fd != targetFD) { dup2(fd, targetFD); close(fd);