From a77aa747eaf779898d401614a11b98fe58dddf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 25 Apr 2018 09:18:34 +0200 Subject: [PATCH] launch_daemon: Added basic logging facility * The daemon now stores many events in am internal log. * You can use "launch_roster log" to retrieve it. --- headers/private/app/LaunchDaemonDefs.h | 3 +- headers/private/app/LaunchRoster.h | 6 +- src/bin/launch_roster.cpp | 140 ++++- src/kits/app/LaunchRoster.cpp | 30 +- src/servers/launch/Events.cpp | 21 +- src/servers/launch/Events.h | 4 +- src/servers/launch/Jamfile | 3 +- src/servers/launch/Job.cpp | 7 +- src/servers/launch/Job.h | 4 +- src/servers/launch/LaunchDaemon.cpp | 134 +++- src/servers/launch/Log.cpp | 810 +++++++++++++++++++++++++ src/servers/launch/Log.h | 95 +++ src/tests/servers/launch/Jamfile | 3 +- 13 files changed, 1227 insertions(+), 33 deletions(-) create mode 100644 src/servers/launch/Log.cpp create mode 100644 src/servers/launch/Log.h diff --git a/headers/private/app/LaunchDaemonDefs.h b/headers/private/app/LaunchDaemonDefs.h index 8f0dc55939..13d7e7cf92 100644 --- a/headers/private/app/LaunchDaemonDefs.h +++ b/headers/private/app/LaunchDaemonDefs.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2017, Haiku, Inc. All Rights Reserved. + * Copyright 2015-2018, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -45,6 +45,7 @@ enum { B_GET_LAUNCH_JOBS = 'lngj', B_GET_LAUNCH_TARGET_INFO = 'lntI', B_GET_LAUNCH_JOB_INFO = 'lnjI', + B_GET_LAUNCH_LOG = 'lnLL', }; diff --git a/headers/private/app/LaunchRoster.h b/headers/private/app/LaunchRoster.h index 93b6bd2a0f..ac2244529d 100644 --- a/headers/private/app/LaunchRoster.h +++ b/headers/private/app/LaunchRoster.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2016 Haiku, Inc. All rights reserved. + * Copyright 2015-2018 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _LAUNCH_ROSTER_H @@ -56,6 +56,9 @@ public: status_t GetJobs(const char* target, BStringList& jobs); status_t GetJobInfo(const char* name, BMessage& info); + status_t GetLog(BMessage& info); + status_t GetLog(const BMessage& filter, BMessage& info); + class Private; private: @@ -70,6 +73,7 @@ private: uint32 flags = 0); status_t _GetInfo(uint32 what, const char* name, BMessage& info); + status_t _GetLog(const BMessage* filter, BMessage& info); private: BMessenger fMessenger; diff --git a/src/bin/launch_roster.cpp b/src/bin/launch_roster.cpp index 3d272c8726..7dbf5b4d9e 100644 --- a/src/bin/launch_roster.cpp +++ b/src/bin/launch_roster.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2016, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -10,11 +10,12 @@ #include #include +#include #include #include #include #include -#include +#include static struct option const kLongOptions[] = { @@ -23,6 +24,16 @@ static struct option const kLongOptions[] = { {NULL} }; +static struct option const kLogLongOptions[] = { + {"help", no_argument, 0, 'h'}, + {"raw", no_argument, 0, 'r'}, + {"user", no_argument, 0, 'u'}, + {"system", no_argument, 0, 's'}, + {"event", required_argument, 0, 'e'}, + {"limit", required_argument, 0, 'l'}, + {NULL} +}; + extern const char *__progname; static const char *kProgramName = __progname; @@ -61,6 +72,127 @@ list_targets(bool verbose) } +static void +print_log(const BMessage& log) +{ + time_t now = time(NULL); + bigtime_t runtime = system_time(); + + for (int32 index = 0;; index++) { + BMessage item; + if (log.FindMessage("item", index, &item) != B_OK) + break; + + uint64 when; + const char* message; + if (item.FindUInt64("when", &when) != B_OK + || item.FindString("message", &message) != B_OK) + break; + + time_t at = now - (runtime - when) / 1000000l; + struct tm tm; + localtime_r(&at, &tm); + char label[256]; + strftime(label, sizeof(label), "%F %X", &tm); + printf("%s %s\n", label, message); + } +} + + +static void +log_usage(int status) +{ + fprintf(stderr, "Usage: %s log [-rusel] []\n" + "Where the following options are allowed:\n" + " -u --user List only user log entries\n" + " -s --system List only system log entries\n" + " -e --event Filter by event name (partial names accepted)\n" + " -l --limit Limit output to events\n" + ", if given, filters the jobs by name.\n", + kProgramName); + + exit(status); +} + + +static void +get_log(int argCount, char** args) +{ + bool raw = false; + bool userOnly = false; + bool systemOnly = false; + int32 limit = 0; + const char* event = NULL; + const char* job = NULL; + + optind = 0; + int c; + while ((c = getopt_long(argCount, args, "hruse:l:", kLogLongOptions, NULL)) + != -1) { + switch (c) { + case 0: + break; + case 'h': + log_usage(0); + break; + case 'r': + raw = true; + break; + case 'u': + userOnly = true; + break; + case 's': + systemOnly = true; + break; + case 'e': + event = optarg; + break; + case 'l': + limit = strtol(optarg, NULL, 0); + break; + } + } + + if (argCount - optind >= 1) + job = args[optind]; + + BLaunchRoster roster; + BMessage filter; + if (userOnly) + filter.AddBool("userOnly", true); + if (systemOnly) + filter.AddBool("systemOnly", true); + if (event != NULL) + filter.AddString("event", event); + if (job != NULL) + filter.AddString("job", job); + if (limit != 0) + filter.AddInt32("limit", limit); + + BMessage info; + status_t status = roster.GetLog(filter, info); + if (status != B_OK) { + fprintf(stderr, "%s: Could not get log: %s\n", kProgramName, + strerror(status)); + exit(EXIT_FAILURE); + } + + if (raw) { + info.PrintToStream(); + return; + } + + print_log(info); + + BMessage user; + if (info.FindMessage("user", &user) == B_OK) { + if (user.HasMessage("item")) + puts("User log:"); + print_log(user); + } +} + + static void get_info(const char* name) { @@ -165,7 +297,7 @@ main(int argc, char** argv) bool verbose = false; int c; - while ((c = getopt_long(argc, argv, "hv", kLongOptions, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "+hv", kLongOptions, NULL)) != -1) { switch (c) { case 0: break; @@ -188,6 +320,8 @@ main(int argc, char** argv) list_jobs(verbose); } else if (strcmp(command, "list-targets") == 0) { list_targets(verbose); + } else if (strcmp(command, "log") == 0) { + get_log(argc - optind, &argv[optind]); } else if (argc == optind + 1) { // For convenience (the "info" command can be omitted) get_info(command); diff --git a/src/kits/app/LaunchRoster.cpp b/src/kits/app/LaunchRoster.cpp index afd09c512e..ad976c3dda 100644 --- a/src/kits/app/LaunchRoster.cpp +++ b/src/kits/app/LaunchRoster.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2016 Haiku, Inc. All rights reserved. + * Copyright 2015-2017 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -338,6 +338,20 @@ BLaunchRoster::GetJobInfo(const char* name, BMessage& info) } +status_t +BLaunchRoster::GetLog(BMessage& info) +{ + return _GetLog(NULL, info); +} + + +status_t +BLaunchRoster::GetLog(const BMessage& filter, BMessage& info) +{ + return _GetLog(&filter, info); +} + + void BLaunchRoster::_InitMessenger() { @@ -414,3 +428,17 @@ BLaunchRoster::_GetInfo(uint32 what, const char* name, BMessage& info) return _SendRequest(request, info); } + + +status_t +BLaunchRoster::_GetLog(const BMessage* filter, BMessage& info) +{ + BMessage request(B_GET_LAUNCH_LOG); + status_t status = request.AddInt32("user", getuid()); + if (status == B_OK && filter != NULL) + status = request.AddMessage("filter", filter); + if (status != B_OK) + return status; + + return _SendRequest(request, info); +} diff --git a/src/servers/launch/Events.cpp b/src/servers/launch/Events.cpp index c5d9aab96c..d3d8e37b4e 100644 --- a/src/servers/launch/Events.cpp +++ b/src/servers/launch/Events.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -39,7 +39,7 @@ public: virtual status_t Register(EventRegistrator& registrator); virtual void Unregister(EventRegistrator& registrator); - virtual void Trigger(); + virtual void Trigger(Event* origin); virtual BaseJob* Owner() const; virtual void SetOwner(BaseJob* owner); @@ -205,11 +205,11 @@ Event::Triggered() const void -Event::Trigger() +Event::Trigger(Event* origin) { fTriggered = true; if (fParent != NULL) - fParent->Trigger(); + fParent->Trigger(origin); } @@ -336,12 +336,13 @@ EventContainer::Unregister(EventRegistrator& registrator) void -EventContainer::Trigger() +EventContainer::Trigger(Event* origin) { - Event::Trigger(); + Event::Trigger(origin); if (Parent() == NULL && Owner() != NULL) { BMessage message(kMsgEventTriggered); + message.AddPointer("event", origin); message.AddString("owner", Owner()->Name()); fTarget.SendMessage(&message); } @@ -620,7 +621,7 @@ VolumeMountedEvent::ToString() const void VolumeMountedEvent::VolumeMounted(dev_t device) { - Trigger(); + Trigger(this); } @@ -667,7 +668,7 @@ void NetworkAvailableEvent::NetworkAvailabilityChanged(bool available) { if (available) - Trigger(); + Trigger(this); else ResetSticky(); } @@ -737,7 +738,7 @@ Events::TriggerExternalEvent(Event* event, const char* name) Event* event = container->Events().ItemAt(index); if (ExternalEvent* external = dynamic_cast(event)) { if (external->Name() == name) { - external->Trigger(); + external->Trigger(container); return; } } else if (dynamic_cast(event) != NULL) { @@ -794,7 +795,7 @@ Events::TriggerDemand(Event* event, bool testOnly) if (testOnly) return true; - childEvent->Trigger(); + childEvent->Trigger(childEvent); break; } if (dynamic_cast(childEvent) != NULL) { diff --git a/src/servers/launch/Events.h b/src/servers/launch/Events.h index 862b5cae2f..cf06118f96 100644 --- a/src/servers/launch/Events.h +++ b/src/servers/launch/Events.h @@ -1,5 +1,5 @@ /* - * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #ifndef EVENTS_H @@ -35,7 +35,7 @@ public: EventRegistrator& registrator) = 0; bool Triggered() const; - virtual void Trigger(); + virtual void Trigger(Event* origin); virtual void ResetTrigger(); virtual BaseJob* Owner() const; diff --git a/src/servers/launch/Jamfile b/src/servers/launch/Jamfile index db2c7921c8..a5796e19e8 100644 --- a/src/servers/launch/Jamfile +++ b/src/servers/launch/Jamfile @@ -1,6 +1,6 @@ SubDir HAIKU_TOP src servers launch ; -UsePrivateHeaders app libroot shared storage support ; +UsePrivateHeaders app kernel libroot shared storage support ; UsePrivateSystemHeaders ; UseHeaders [ FDirName $(HAIKU_TOP) src bin multiuser ] ; @@ -13,6 +13,7 @@ Server launch_daemon Conditions.cpp Events.cpp Job.cpp + Log.cpp NetworkWatcher.cpp SettingsParser.cpp Target.cpp diff --git a/src/servers/launch/Job.cpp b/src/servers/launch/Job.cpp index 08ee72eb3a..9188f50a51 100644 --- a/src/servers/launch/Job.cpp +++ b/src/servers/launch/Job.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2016, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -34,7 +34,8 @@ Job::Job(const char* name) fToken((uint32)B_PREFERRED_TOKEN), fLaunchStatus(B_NO_INIT), fTarget(NULL), - fPendingLaunchDataReplies(0, false) + fPendingLaunchDataReplies(0, false), + fTeamListener(NULL) { mutex_init(&fLaunchStatusLock, "launch status lock"); } @@ -694,7 +695,7 @@ Job::_Launch(const char* signature, entry_ref* ref, int argCount, resume_thread(mainThread); if (fTeamListener != NULL) - fTeamListener->TeamLaunched(this); + fTeamListener->TeamLaunched(this, result); } else kill_thread(mainThread); } diff --git a/src/servers/launch/Job.h b/src/servers/launch/Job.h index 189dd17557..aac20fcae5 100644 --- a/src/servers/launch/Job.h +++ b/src/servers/launch/Job.h @@ -1,5 +1,5 @@ /* - * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ #ifndef JOB_H @@ -34,7 +34,7 @@ typedef std::map PortMap; class TeamListener { public: - virtual void TeamLaunched(Job* job) = 0; + virtual void TeamLaunched(Job* job, status_t status) = 0; }; diff --git a/src/servers/launch/LaunchDaemon.cpp b/src/servers/launch/LaunchDaemon.cpp index 4a628b73c7..6d00ed052d 100644 --- a/src/servers/launch/LaunchDaemon.cpp +++ b/src/servers/launch/LaunchDaemon.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2015-2017, Axel Dörfler, axeld@pinc-software.de. + * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. * Distributed under the terms of the MIT License. */ @@ -41,6 +41,7 @@ #include "InitSharedMemoryDirectoryJob.h" #include "InitTemporaryDirectoryJob.h" #include "Job.h" +#include "Log.h" #include "SettingsParser.h" #include "Target.h" #include "Utility.h" @@ -147,7 +148,7 @@ public: const char* name); // TeamListener - virtual void TeamListener(Job* job); + virtual void TeamLaunched(Job* job, status_t status); virtual void ReadyToRun(); virtual void MessageReceived(BMessage* message); @@ -170,6 +171,7 @@ private: void _HandleGetLaunchTargetInfo(BMessage* message); void _HandleGetLaunchJobs(BMessage* message); void _HandleGetLaunchJobInfo(BMessage* message); + void _HandleGetLaunchLog(BMessage* message); uid_t _GetUserID(BMessage* message); void _ReadPaths(const BStringList& paths); @@ -222,6 +224,7 @@ private: void _AddInitJob(BJob* job); private: + Log fLog; JobMap fJobs; TargetMap fTargets; BStringList fRunTargets; @@ -425,8 +428,10 @@ LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name) void -LaunchDaemon::RegisterTeam(Job* job) +LaunchDaemon::TeamLaunched(Job* job, status_t status) { + fLog.JobLaunched(job, status); + MutexLocker locker(fTeamsLock); fTeams.insert(std::make_pair(job->Team(), job)); } @@ -514,6 +519,11 @@ LaunchDaemon::MessageReceived(BMessage* message) if (found != fTeams.end()) { Job* job = found->second; TRACE("Job %s ended!\n", job->Name()); + + // Get the exit status, and pass it on to the log + status_t exitStatus = B_OK; + wait_for_thread(team, &exitStatus); + fLog.JobTerminated(job, exitStatus); job->TeamDeleted(); if (job->IsService()) { @@ -613,22 +623,30 @@ LaunchDaemon::MessageReceived(BMessage* message) _HandleGetLaunchJobInfo(message); break; + case B_GET_LAUNCH_LOG: + _HandleGetLaunchLog(message); + break; + case kMsgEventTriggered: { // An internal event has been triggered. - // Check if its job can be launched now. + // Check if its job(s) can be launched now. const char* name = message->GetString("owner"); if (name == NULL) break; + Event* event = (Event*)message->GetPointer("event"); + Job* job = FindJob(name); if (job != NULL) { + fLog.EventTriggered(job, event); _LaunchJob(job); break; } Target* target = FindTarget(name); if (target != NULL) { + fLog.EventTriggered(target, event); _LaunchJobs(target); break; } @@ -785,7 +803,9 @@ LaunchDaemon::_HandleStopLaunchTarget(BMessage* message) if (message->FindMessage("data", &data) == B_OK) target->AddData(data.GetString("name"), data); - _StopJobs(target, message->GetBool("force")); + bool force = message->GetBool("force"); + fLog.JobStopped(target, force); + _StopJobs(target, force); BMessage reply((uint32)B_OK); message->SendReply(&reply); @@ -848,6 +868,7 @@ LaunchDaemon::_HandleEnableLaunchJob(BMessage* message) } job->SetEnabled(enable); + fLog.JobEnabled(job, enable); BMessage reply((uint32)B_OK); message->SendReply(&reply); @@ -877,7 +898,9 @@ LaunchDaemon::_HandleStopLaunchJob(BMessage* message) return; } - _StopJob(job, message->GetBool("force")); + bool force = message->GetBool("force"); + fLog.JobStopped(job, force); + _StopJob(job, force); BMessage reply((uint32)B_OK); message->SendReply(&reply); @@ -965,6 +988,8 @@ LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message) eventName.Prepend(ownerName); fEvents.insert(std::make_pair(eventName, event)); _ResolveExternalEvents(event, eventName); + + fLog.ExternalEventRegistered(name); } else status = B_NO_MEMORY; } else @@ -1002,6 +1027,8 @@ LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message) eventName.Prepend("/"); eventName.Prepend(ownerName); fEvents.erase(eventName); + + fLog.ExternalEventRegistered(name); } else status = B_BAD_VALUE; @@ -1028,6 +1055,8 @@ LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message) ExternalEventSource* event = _FindEvent(ownerName, name); if (event != NULL) { + fLog.ExternalEventTriggered(name); + // Evaluate all of its jobs int32 count = event->CountListeners(); for (int32 index = 0; index < count; index++) { @@ -1223,6 +1252,90 @@ LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message) } +void +LaunchDaemon::_HandleGetLaunchLog(BMessage* message) +{ + uid_t user = _GetUserID(message); + if (user < 0) + return; + + BMessage filter; + BString jobName; + const char* event = NULL; + int32 limit = 0; + bool systemOnly = false; + bool userOnly = false; + if (message->FindMessage("filter", &filter) == B_OK) { + limit = filter.GetInt32("limit", 0); + jobName = filter.GetString("job"); + jobName.ToLower(); + event = filter.GetString("event"); + systemOnly = filter.GetBool("systemOnly"); + userOnly = filter.GetBool("userOnly"); + } + + BMessage info((uint32)B_OK); + int32 count = 0; + + if (user == 0 || !userOnly) { + LogItemList::Iterator iterator = fLog.Iterator(); + while (iterator.HasNext()) { + LogItem* item = iterator.Next(); + if (!item->Matches(jobName.IsEmpty() ? NULL : jobName.String(), + event)) { + continue; + } + + BMessage itemMessage; + itemMessage.AddUInt64("when", item->When()); + itemMessage.AddInt32("type", (int32)item->Type()); + itemMessage.AddString("message", item->Message()); + + BMessage parameter; + item->GetParameter(parameter); + itemMessage.AddMessage("parameter", ¶meter); + + info.AddMessage("item", &itemMessage); + + // limit == 0 means no limit + if (++count == limit) + break; + } + } + + // Get the list from the user daemon, and merge it into our reply + Session* session = FindSession(user); + if (session != NULL && !systemOnly) { + if (limit != 0) { + // Update limit for user daemon + limit -= count; + if (limit <= 0) { + message->SendReply(&info); + return; + } + } + + BMessage reply; + + BMessage request(B_GET_LAUNCH_LOG); + status_t status = request.AddInt32("user", 0); + if (status == B_OK && (limit != 0 || !jobName.IsEmpty() + || event != NULL)) { + // Forward filter specification when needed + status = filter.SetInt32("limit", limit); + if (status == B_OK) + status = request.AddMessage("filter", &filter); + } + if (status == B_OK) + status = session->Daemon().SendMessage(&request, &reply); + if (status == B_OK) + info.AddMessage("user", &reply); + } + + message->SendReply(&info); +} + + uid_t LaunchDaemon::_GetUserID(BMessage* message) { @@ -1419,8 +1532,10 @@ LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) } else TRACE(" amend job \"%s\"\n", name.String()); - if (message.HasBool("disabled")) + if (message.HasBool("disabled")) { job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled())); + fLog.JobEnabled(job, job->IsEnabled()); + } if (message.HasBool("legacy")) job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); @@ -1477,12 +1592,15 @@ LaunchDaemon::_InitJobs(Target* target) } } - if (status != B_OK) { + if (status == B_OK) { + fLog.JobInitialized(job); + } else { if (status != B_NO_INIT) { // TODO: log error debug_printf("Init \"%s\" failed: %s\n", job->Name(), strerror(status)); } + fLog.JobIgnored(job, status); // Remove jobs that won't be used later on fJobs.erase(remove); diff --git a/src/servers/launch/Log.cpp b/src/servers/launch/Log.cpp new file mode 100644 index 0000000000..8f69c3ae99 --- /dev/null +++ b/src/servers/launch/Log.cpp @@ -0,0 +1,810 @@ +/* + * Copyright 2017-2018, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Log.h" + +#include + +#include "Events.h" +#include "Job.h" + + +const size_t kMaxItems = 10000; + + +class AbstractJobLogItem : public LogItem { +public: + AbstractJobLogItem(BaseJob* job); + virtual ~AbstractJobLogItem(); + + virtual status_t GetParameter(BMessage& parameter) const; + virtual bool Matches(const char* jobName, + const char* eventName); + +protected: + BaseJob* fJob; +}; + + +class JobInitializedLogItem : public AbstractJobLogItem { +public: + JobInitializedLogItem(Job* job); + virtual ~JobInitializedLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; +}; + + +class JobIgnoredLogItem : public LogItem { +public: + JobIgnoredLogItem(Job* job, status_t error); + virtual ~JobIgnoredLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; + virtual status_t GetParameter(BMessage& parameter) const; + virtual bool Matches(const char* jobName, + const char* eventName); + +private: + BString fJobName; + status_t fError; +}; + + +class JobLaunchedLogItem : public AbstractJobLogItem { +public: + JobLaunchedLogItem(Job* job, status_t status); + virtual ~JobLaunchedLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; + virtual status_t GetParameter(BMessage& parameter) const; + +private: + status_t fStatus; +}; + + +class JobTerminatedLogItem : public AbstractJobLogItem { +public: + JobTerminatedLogItem(Job* job, status_t status); + virtual ~JobTerminatedLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; + virtual status_t GetParameter(BMessage& parameter) const; + +private: + status_t fStatus; +}; + + +class JobEnabledLogItem : public AbstractJobLogItem { +public: + JobEnabledLogItem(Job* job, bool enabled); + virtual ~JobEnabledLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; + virtual status_t GetParameter(BMessage& parameter) const; + +private: + bool fEnabled; +}; + + +class JobStoppedLogItem : public AbstractJobLogItem { +public: + JobStoppedLogItem(BaseJob* job, bool force); + virtual ~JobStoppedLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; + virtual status_t GetParameter(BMessage& parameter) const; + +private: + bool fForce; +}; + + +class EventLogItem : public AbstractJobLogItem { +public: + EventLogItem(BaseJob* job, Event* event); + virtual ~EventLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; + virtual status_t GetParameter(BMessage& parameter) const; + virtual bool Matches(const char* jobName, + const char* eventName); + +private: + Event* fEvent; +}; + + +class AbstractExternalEventLogItem : public LogItem { +public: + AbstractExternalEventLogItem(const char* name); + virtual ~AbstractExternalEventLogItem(); + + virtual status_t GetParameter(BMessage& parameter) const; + virtual bool Matches(const char* jobName, + const char* eventName); + +protected: + BString fEventName; +}; + + +class ExternalEventLogItem : public AbstractExternalEventLogItem { +public: + ExternalEventLogItem(const char* name); + virtual ~ExternalEventLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; +}; + + +class ExternalEventRegisteredLogItem : public AbstractExternalEventLogItem { +public: + ExternalEventRegisteredLogItem( + const char* name); + virtual ~ExternalEventRegisteredLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; +}; + + +class ExternalEventUnregisteredLogItem : public AbstractExternalEventLogItem { +public: + ExternalEventUnregisteredLogItem( + const char* name); + virtual ~ExternalEventUnregisteredLogItem(); + + virtual LogItemType Type() const; + virtual status_t GetMessage(BString& target) const; +}; + + +// #pragma mark - + + +LogItem::LogItem() + : + fWhen(system_time()) +{ +} + + +LogItem::~LogItem() +{ +} + + +BString +LogItem::Message() const +{ + BString message; + GetMessage(message); + return message; +} + + +// #pragma mark - Log + + +Log::Log() + : + fCount(0) +{ + mutex_init(&fLock, "log lock"); +} + + +void +Log::Add(LogItem* item) +{ + MutexLocker locker(fLock); + if (fCount == kMaxItems) + fItems.Remove(fItems.First()); + else + fCount++; + + fItems.Add(item); +} + + +void +Log::JobInitialized(Job* job) +{ + LogItem* item = new(std::nothrow) JobInitializedLogItem(job); + if (item != NULL) + Add(item); + else + debug_printf("Initialized job \"%s\"\n", job->Name()); +} + + +void +Log::JobIgnored(Job* job, status_t status) +{ + LogItem* item = new(std::nothrow) JobIgnoredLogItem(job, status); + if (item != NULL) + Add(item); + else { + debug_printf("Ignored job \"%s\": %s\n", job->Name(), + strerror(status)); + } +} + + +void +Log::JobLaunched(Job* job, status_t status) +{ + LogItem* item = new(std::nothrow) JobLaunchedLogItem(job, status); + if (item != NULL) + Add(item); + else { + debug_printf("Launched job \"%s\": %s\n", job->Name(), + strerror(status)); + } +} + + +void +Log::JobTerminated(Job* job, status_t status) +{ + LogItem* item = new(std::nothrow) JobTerminatedLogItem(job, status); + if (item != NULL) + Add(item); + else { + debug_printf("Terminated job \"%s\": %s\n", job->Name(), + strerror(status)); + } +} + + +void +Log::JobEnabled(Job* job, bool enabled) +{ + LogItem* item = new(std::nothrow) JobEnabledLogItem(job, enabled); + if (item != NULL) + Add(item); + else + debug_printf("Enabled job \"%s\": %d\n", job->Name(), enabled); +} + + +void +Log::JobStopped(BaseJob* job, bool force) +{ + LogItem* item = new(std::nothrow) JobStoppedLogItem(job, force); + if (item != NULL) + Add(item); + else + debug_printf("Stopped job \"%s\"\n", job->Name()); +} + + +void +Log::EventTriggered(BaseJob* job, Event* event) +{ + LogItem* item = new(std::nothrow) EventLogItem(job, event); + if (item != NULL) + Add(item); + else { + debug_printf("Event triggered for \"%s\": %s\n", job->Name(), + event->ToString().String()); + } +} + + +void +Log::ExternalEventTriggered(const char* name) +{ + LogItem* item = new(std::nothrow) ExternalEventLogItem(name); + if (item != NULL) + Add(item); + else + debug_printf("External event triggered: %s\n", name); +} + + +void +Log::ExternalEventRegistered(const char* name) +{ + LogItem* item = new(std::nothrow) ExternalEventRegisteredLogItem(name); + if (item != NULL) + Add(item); + else + debug_printf("External event registered: %s\n", name); +} + + +void +Log::ExternalEventUnregistered(const char* name) +{ + LogItem* item = new(std::nothrow) ExternalEventUnregisteredLogItem(name); + if (item != NULL) + Add(item); + else + debug_printf("External event unregistered: %s\n", name); +} + + +// #pragma mark - AbstractJobLogItem + + +AbstractJobLogItem::AbstractJobLogItem(BaseJob* job) + : + fJob(job) +{ +} + + +AbstractJobLogItem::~AbstractJobLogItem() +{ +} + + +status_t +AbstractJobLogItem::GetParameter(BMessage& parameter) const +{ + return parameter.AddString("job", fJob->Name()); +} + + +bool +AbstractJobLogItem::Matches(const char* jobName, const char* eventName) +{ + if (jobName == NULL && eventName == NULL) + return true; + + if (jobName != NULL && strcmp(fJob->Name(), jobName) == 0) + return true; + + return false; +} + + +// #pragma mark - JobInitializedLogItem + + +JobInitializedLogItem::JobInitializedLogItem(Job* job) + : + AbstractJobLogItem(job) +{ +} + + +JobInitializedLogItem::~JobInitializedLogItem() +{ +} + + +LogItemType +JobInitializedLogItem::Type() const +{ + return kJobInitialized; +} + + +status_t +JobInitializedLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Job \"%s\" initialized.", fJob->Name()); + return B_OK; +} + + +// #pragma mark - JobIgnoredLogItem + + +JobIgnoredLogItem::JobIgnoredLogItem(Job* job, status_t error) + : + fJobName(job->Name()), + fError(error) +{ +} + + +JobIgnoredLogItem::~JobIgnoredLogItem() +{ +} + + +LogItemType +JobIgnoredLogItem::Type() const +{ + return kJobIgnored; +} + + +status_t +JobIgnoredLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Ignored job \"%s\" due %s", fJobName.String(), + strerror(fError)); + return B_OK; +} + + +status_t +JobIgnoredLogItem::GetParameter(BMessage& parameter) const +{ + status_t status = parameter.AddString("job", fJobName); + if (status == B_OK) + status = parameter.AddInt32("error", fError); + return status; +} + + +bool +JobIgnoredLogItem::Matches(const char* jobName, const char* eventName) +{ + if (jobName == NULL && eventName == NULL) + return true; + + if (jobName != NULL && fJobName == jobName) + return true; + + return false; +} + + +// #pragma mark - JobLaunchedLogItem + + +JobLaunchedLogItem::JobLaunchedLogItem(Job* job, status_t status) + : + AbstractJobLogItem(job), + fStatus(status) +{ +} + + +JobLaunchedLogItem::~JobLaunchedLogItem() +{ +} + + +LogItemType +JobLaunchedLogItem::Type() const +{ + return kJobLaunched; +} + + +status_t +JobLaunchedLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Job \"%s\" launched: %s", fJob->Name(), + strerror(fStatus)); + return B_OK; +} + + +status_t +JobLaunchedLogItem::GetParameter(BMessage& parameter) const +{ + status_t status = AbstractJobLogItem::GetParameter(parameter); + if (status == B_OK) + status = parameter.AddInt32("status", fStatus); + return status; +} + + +// #pragma mark - JobTerminatedLogItem + + +JobTerminatedLogItem::JobTerminatedLogItem(Job* job, status_t status) + : + AbstractJobLogItem(job), + fStatus(status) +{ +} + + +JobTerminatedLogItem::~JobTerminatedLogItem() +{ +} + + +LogItemType +JobTerminatedLogItem::Type() const +{ + return kJobTerminated; +} + + +status_t +JobTerminatedLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Job \"%s\" terminated: %s", fJob->Name(), + strerror(fStatus)); + return B_OK; +} + + +status_t +JobTerminatedLogItem::GetParameter(BMessage& parameter) const +{ + status_t status = AbstractJobLogItem::GetParameter(parameter); + if (status == B_OK) + status = parameter.AddInt32("status", fStatus); + return status; +} + + +// #pragma mark - JobEnabledLogItem + + +JobEnabledLogItem::JobEnabledLogItem(Job* job, bool enabled) + : + AbstractJobLogItem(job), + fEnabled(enabled) +{ +} + + +JobEnabledLogItem::~JobEnabledLogItem() +{ +} + + +LogItemType +JobEnabledLogItem::Type() const +{ + return kJobEnabled; +} + + +status_t +JobEnabledLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Job \"%s\" %sabled", fJob->Name(), + fEnabled ? "en" : "dis"); + return B_OK; +} + + +status_t +JobEnabledLogItem::GetParameter(BMessage& parameter) const +{ + status_t status = AbstractJobLogItem::GetParameter(parameter); + if (status == B_OK) + status = parameter.AddBool("enabled", fEnabled); + return status; +} + + +// #pragma mark - JobStoppedLogItem + + +JobStoppedLogItem::JobStoppedLogItem(BaseJob* job, bool force) + : + AbstractJobLogItem(job), + fForce(force) +{ +} + + +JobStoppedLogItem::~JobStoppedLogItem() +{ +} + + +LogItemType +JobStoppedLogItem::Type() const +{ + return kJobStopped; +} + + +status_t +JobStoppedLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Job \"%s\" %sstopped", fJob->Name(), + fForce ? "force " : ""); + return B_OK; +} + + +status_t +JobStoppedLogItem::GetParameter(BMessage& parameter) const +{ + status_t status = AbstractJobLogItem::GetParameter(parameter); + if (status == B_OK) + status = parameter.AddBool("force", fForce); + return status; +} + + +// #pragma mark - EventLogItem + + +EventLogItem::EventLogItem(BaseJob* job, Event* event) + : + AbstractJobLogItem(job), + fEvent(event) +{ +} + + +EventLogItem::~EventLogItem() +{ +} + + +LogItemType +EventLogItem::Type() const +{ + return kEvent; +} + + +status_t +EventLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("Event triggered \"%s\": \"%s\"", fJob->Name(), + fEvent->ToString().String()); + return B_OK; +} + + +status_t +EventLogItem::GetParameter(BMessage& parameter) const +{ + status_t status = AbstractJobLogItem::GetParameter(parameter); + if (status == B_OK) + status = parameter.AddString("event", fEvent->ToString()); + return status; +} + + +bool +EventLogItem::Matches(const char* jobName, const char* eventName) +{ + if (eventName != NULL && strstr(fEvent->ToString(), eventName) == NULL) + return false; + + return AbstractJobLogItem::Matches(jobName, NULL); +} + + +// #pragma mark - ExternalEventLogItem + + +AbstractExternalEventLogItem::AbstractExternalEventLogItem(const char* name) + : + fEventName(name) +{ +} + + +AbstractExternalEventLogItem::~AbstractExternalEventLogItem() +{ +} + + +status_t +AbstractExternalEventLogItem::GetParameter(BMessage& parameter) const +{ + return parameter.AddString("event", fEventName); +} + + +bool +AbstractExternalEventLogItem::Matches(const char* jobName, + const char* eventName) +{ + if (jobName == NULL && eventName == NULL) + return true; + + if (eventName != NULL && strstr(fEventName.String(), eventName) != NULL) + return true; + + return false; +} + + +// #pragma mark - ExternalEventLogItem + + +ExternalEventLogItem::ExternalEventLogItem(const char* name) + : + AbstractExternalEventLogItem(name) +{ +} + + +ExternalEventLogItem::~ExternalEventLogItem() +{ +} + + +LogItemType +ExternalEventLogItem::Type() const +{ + return kExternalEvent; +} + + +status_t +ExternalEventLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("External event triggered: \"%s\"", + fEventName.String()); + return B_OK; +} + + +// #pragma mark - ExternalEventRegisteredLogItem + + +ExternalEventRegisteredLogItem::ExternalEventRegisteredLogItem(const char* name) + : + AbstractExternalEventLogItem(name) +{ +} + + +ExternalEventRegisteredLogItem::~ExternalEventRegisteredLogItem() +{ +} + + +LogItemType +ExternalEventRegisteredLogItem::Type() const +{ + return kExternalEventRegistered; +} + + +status_t +ExternalEventRegisteredLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("External event registered: \"%s\"", + fEventName.String()); + return B_OK; +} + + +// #pragma mark - ExternalEventUnregisteredLogItem + + +ExternalEventUnregisteredLogItem::ExternalEventUnregisteredLogItem( + const char* name) + : + AbstractExternalEventLogItem(name) +{ +} + + +ExternalEventUnregisteredLogItem::~ExternalEventUnregisteredLogItem() +{ +} + + +LogItemType +ExternalEventUnregisteredLogItem::Type() const +{ + return kExternalEventUnregistered; +} + + +status_t +ExternalEventUnregisteredLogItem::GetMessage(BString& target) const +{ + target.SetToFormat("External event unregistered: \"%s\"", + fEventName.String()); + return B_OK; +} diff --git a/src/servers/launch/Log.h b/src/servers/launch/Log.h new file mode 100644 index 0000000000..665aaeb359 --- /dev/null +++ b/src/servers/launch/Log.h @@ -0,0 +1,95 @@ +/* + * Copyright 2017-2018, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef LOG_H +#define LOG_H + + +#include + +#include +#include + + +class BMessage; + +class BaseJob; +class Event; +class Job; + + +enum LogItemType { + kJobInitialized, + kJobIgnored, + kJobLaunched, + kJobTerminated, + kJobEnabled, + kJobStopped, + kEvent, + kExternalEvent, + kExternalEventRegistered, + kExternalEventUnregistered, +}; + + +class LogItem : public DoublyLinkedListLinkImpl { +public: +public: + LogItem(); + virtual ~LogItem(); + + bigtime_t When() + { return fWhen; } + BString Message() const; + + virtual LogItemType Type() const = 0; + virtual status_t GetMessage(BString& target) const = 0; + virtual status_t GetParameter(BMessage& parameter) const = 0; + virtual bool Matches(const char* jobName, + const char* eventName) = 0; + +private: + bigtime_t fWhen; +}; + + +typedef DoublyLinkedList LogItemList; + + +class Log { +public: + Log(); + + void Add(LogItem* item); + + LogItemList::Iterator + Iterator() + { return fItems.GetIterator(); } + + mutex& Lock() + { return fLock; } + + void JobInitialized(Job* job); + void JobIgnored(Job* job, status_t status); + + void JobLaunched(Job* job, status_t status); + void JobTerminated(Job* job, status_t status); + + void JobEnabled(Job* job, bool enabled); + void JobStopped(BaseJob* job, bool force); + + void EventTriggered(BaseJob* job, Event* event); + + void ExternalEventTriggered(const char* name); + void ExternalEventRegistered(const char* name); + void ExternalEventUnregistered(const char* name); + +private: + mutex fLock; + LogItemList fItems; + size_t fCount; +}; + + +#endif // LOG_H diff --git a/src/tests/servers/launch/Jamfile b/src/tests/servers/launch/Jamfile index 7788bfdfc4..cb44f936b9 100644 --- a/src/tests/servers/launch/Jamfile +++ b/src/tests/servers/launch/Jamfile @@ -2,7 +2,7 @@ SubDir HAIKU_TOP src tests servers launch ; AddSubDirSupportedPlatforms libbe_test ; -UsePrivateHeaders app libroot shared storage support ; +UsePrivateHeaders app kernel libroot shared storage support ; UsePrivateSystemHeaders ; UseHeaders [ FDirName $(HAIKU_TOP) src bin multiuser ] ; @@ -39,6 +39,7 @@ Server test_launch_daemon : Conditions.cpp Events.cpp Job.cpp + Log.cpp NetworkWatcher.cpp SettingsParser.cpp Target.cpp