13229Spst/* 23229Spst * Copyright 2015-2018, Axel D��rfler, axeld@pinc-software.de. 33229Spst * Distributed under the terms of the MIT License. 43229Spst */ 53229Spst 63229Spst 73229Spst#include "LaunchDaemon.h" 83229Spst 93229Spst#include <map> 103229Spst#include <set> 113229Spst 123229Spst#include <errno.h> 133229Spst#include <grp.h> 143229Spst#include <spawn.h> 153229Spst#include <stdio.h> 163229Spst#include <stdlib.h> 173229Spst#include <unistd.h> 183229Spst 193229Spst#include <Directory.h> 203229Spst#include <driver_settings.h> 213229Spst#include <Entry.h> 223229Spst#include <File.h> 233229Spst#include <ObjectList.h> 243229Spst#include <Path.h> 2518471Swosch#include <PathFinder.h> 2650476Speter#include <Server.h> 273229Spst 283229Spst#include <AppMisc.h> 293229Spst#include <LaunchDaemonDefs.h> 303229Spst#include <LaunchRosterPrivate.h> 313229Spst#include <locks.h> 323229Spst#include <MessengerPrivate.h> 333229Spst#include <RosterPrivate.h> 3413572Spst#include <syscalls.h> 3513572Spst#include <system_info.h> 363229Spst 373229Spst#include "multiuser_utils.h" 3813572Spst 393229Spst#include "Conditions.h" 403229Spst#include "Events.h" 413229Spst#include "InitRealTimeClockJob.h" 423229Spst#include "InitSharedMemoryDirectoryJob.h" 433229Spst#include "InitTemporaryDirectoryJob.h" 443229Spst#include "Job.h" 453229Spst#include "Log.h" 4613572Spst#include "SettingsParser.h" 473229Spst#include "Target.h" 483229Spst#include "Utility.h" 493229Spst#include "Worker.h" 503229Spst 513229Spst 523229Spst#ifdef DEBUG 533229Spst# define TRACE(x, ...) debug_printf(x, __VA_ARGS__) 543229Spst# define TRACE_ONLY 553229Spst#else 563229Spst# define TRACE(x, ...) ; 573229Spst# define TRACE_ONLY __attribute__((unused)) 583229Spst#endif 593229Spst 603229Spst 613229Spstusing namespace ::BPrivate; 623229Spstusing namespace BSupportKit; 633229Spstusing BSupportKit::BPrivate::JobQueue; 643229Spst 653229Spst 663229Spst#ifndef TEST_MODE 673229Spststatic const char* kLaunchDirectory = "launch"; 683229Spststatic const char* kUserLaunchDirectory = "user_launch"; 693229Spst#endif 703229Spst 713229Spst 723229Spstenum launch_options { 733229Spst FORCE_NOW = 0x01, 743229Spst TRIGGER_DEMAND = 0x02 753229Spst}; 763229Spst 773229Spst 783229Spstclass Session { 793229Spstpublic: 803229Spst Session(uid_t user, const BMessenger& target); 813229Spst 823229Spst uid_t User() const 833229Spst { return fUser; } 843229Spst const BMessenger& Daemon() const 853229Spst { return fDaemon; } 863229Spst 873229Spstprivate: 883229Spst uid_t fUser; 893229Spst BMessenger fDaemon; 903229Spst}; 913229Spst 923229Spst 933229Spst/*! This class is the connection between the external events that are part of 943229Spst a job, and the external event source. 953229Spst 963229Spst There is one object per registered event source, and it keeps all jobs that 973229Spst reference the event as listeners. If the event source triggers the event, 983229Spst the object will be used to trigger the jobs. 993229Spst*/ 1003229Spstclass ExternalEventSource { 1013229Spstpublic: 1023229Spst ExternalEventSource(BMessenger& source, 1033229Spst const char* ownerName, 1043229Spst const char* name, uint32 flags); 1053229Spst ~ExternalEventSource(); 1063229Spst 1073229Spst const BMessenger& Source() const 1083229Spst { return fSource; } 1093229Spst 1103229Spst const char* Name() const; 1113229Spst const char* OwnerName() const; 1123229Spst uint32 Flags() const 1133229Spst { return fFlags; } 1143229Spst 1153229Spst void Trigger(); 1163229Spst bool StickyTriggered() const 1173229Spst { return fStickyTriggered; } 1183229Spst void ResetSticky(); 1193229Spst 1203229Spst status_t AddDestination(Event* event); 1213229Spst void RemoveDestination(Event* event); 1223229Spst 12384125Siedowseprivate: 1243229Spst BMessenger fSource; 1253229Spst BString fName, fOwnerName; 1263229Spst uint32 fFlags; 1273229Spst BObjectList<Event> fDestinations; 1283229Spst bool fStickyTriggered; 1293229Spst}; 1303229Spst 1313229Spst 1323229Spsttypedef std::map<BString, Job*> JobMap; 1333229Spsttypedef std::map<uid_t, Session*> SessionMap; 1343229Spsttypedef std::map<BString, Target*> TargetMap; 1353229Spsttypedef std::map<BString, ExternalEventSource*> EventMap; 1363229Spsttypedef std::map<team_id, Job*> TeamMap; 1373229Spst 1383229Spst 1393229Spstclass LaunchDaemon : public BServer, public Finder, public ConditionContext, 1403229Spst public EventRegistrator, public TeamListener { 1413229Spstpublic: 1423229Spst LaunchDaemon(bool userMode, status_t& error); 1433229Spst virtual ~LaunchDaemon(); 1443229Spst 1453229Spst virtual Job* FindJob(const char* name) const; 1463229Spst virtual Target* FindTarget(const char* name) const; 1473229Spst Session* FindSession(uid_t user) const; 1483229Spst 1493229Spst // ConditionContext 1503229Spst virtual bool IsSafeMode() const; 1513229Spst virtual bool BootVolumeIsReadOnly() const; 1523229Spst 1533229Spst // EventRegistrator 1543229Spst virtual status_t RegisterExternalEvent(Event* event, 1553229Spst const char* name, 1563229Spst const BStringList& arguments); 1573229Spst virtual void UnregisterExternalEvent(Event* event, 1583229Spst const char* name); 1593229Spst 1603229Spst // TeamListener 1613229Spst virtual void TeamLaunched(Job* job, status_t status); 1623229Spst 1633229Spst virtual void ReadyToRun(); 1643229Spst virtual void MessageReceived(BMessage* message); 1653229Spst 1663229Spstprivate: 1673229Spst void _HandleGetLaunchData(BMessage* message); 1683229Spst void _HandleLaunchTarget(BMessage* message); 1693229Spst void _HandleStopLaunchTarget(BMessage* message); 1703229Spst void _HandleLaunchJob(BMessage* message); 1713229Spst void _HandleEnableLaunchJob(BMessage* message); 1723229Spst void _HandleStopLaunchJob(BMessage* message); 1733229Spst void _HandleLaunchSession(BMessage* message); 1743229Spst void _HandleRegisterSessionDaemon(BMessage* message); 1753229Spst void _HandleRegisterLaunchEvent(BMessage* message); 1763229Spst void _HandleUnregisterLaunchEvent(BMessage* message); 1773229Spst void _HandleNotifyLaunchEvent(BMessage* message); 1783229Spst void _HandleResetStickyLaunchEvent( 1793229Spst BMessage* message); 1803229Spst void _HandleGetLaunchTargets(BMessage* message); 1813229Spst void _HandleGetLaunchTargetInfo(BMessage* message); 1823229Spst void _HandleGetLaunchJobs(BMessage* message); 1833229Spst void _HandleGetLaunchJobInfo(BMessage* message); 1843229Spst void _HandleGetLaunchLog(BMessage* message); 1853229Spst uid_t _GetUserID(BMessage* message); 1863229Spst 1873229Spst void _ReadPaths(const BStringList& paths); 1883229Spst void _ReadEntry(const char* context, BEntry& entry); 1893229Spst void _ReadDirectory(const char* context, 190229780Suqs BEntry& directory); 1913229Spst status_t _ReadFile(const char* context, BEntry& entry); 1923229Spst 1933229Spst void _AddJobs(Target* target, BMessage& message); 1943229Spst void _AddTargets(BMessage& message); 1953229Spst void _AddRunTargets(BMessage& message); 1963229Spst void _AddRunTargets(BMessage& message, 1973229Spst const char* name); 1983229Spst void _AddJob(Target* target, bool service, 1993229Spst BMessage& message); 2003229Spst void _InitJobs(Target* target); 2013229Spst void _LaunchJobs(Target* target, 2023229Spst bool forceNow = false); 2033229Spst void _StopJobs(Target* target, bool force); 2043229Spst bool _CanLaunchJob(Job* job, uint32 options, 2053229Spst bool testOnly = false); 2063229Spst bool _CanLaunchJobRequirements(Job* job, 2073229Spst uint32 options); 2083229Spst bool _LaunchJob(Job* job, uint32 options = 0); 2093229Spst void _StopJob(Job* job, bool force); 2103229Spst void _AddTarget(Target* target); 2113229Spst void _SetCondition(BaseJob* job, 2123229Spst const BMessage& message); 2133229Spst void _SetEvent(BaseJob* job, 2143229Spst const BMessage& message); 2153229Spst void _SetEnvironment(BaseJob* job, 2163229Spst const BMessage& message); 2173229Spst 2183229Spst ExternalEventSource* 2193229Spst _FindExternalEventSource(const char* owner, 2203229Spst const char* name) const; 2213229Spst void _ResolveExternalEvents( 2223229Spst ExternalEventSource* event, 2233229Spst const BString& name); 2243229Spst void _GetBaseJobInfo(BaseJob* job, BMessage& info); 2253229Spst void _ForwardEventMessage(uid_t user, 2263229Spst BMessage* message); 2273229Spst 2283229Spst status_t _StartSession(const char* login); 2293229Spst 2303229Spst void _RetrieveKernelOptions(); 2313229Spst void _SetupEnvironment(); 2323229Spst void _InitSystem(); 2333229Spst void _AddInitJob(BJob* job); 2343229Spst 2353229Spstprivate: 2363229Spst Log fLog; 2373229Spst JobMap fJobs; 2383229Spst TargetMap fTargets; 2393229Spst BStringList fRunTargets; 2403229Spst EventMap fEvents; 2413229Spst JobQueue fJobQueue; 2423229Spst SessionMap fSessions; 2433229Spst MainWorker* fMainWorker; 2443229Spst Target* fInitTarget; 2453229Spst TeamMap fTeams; 2463229Spst mutex fTeamsLock; 2473229Spst bool fSafeMode; 2483229Spst bool fReadOnlyBootVolume; 2493229Spst bool fUserMode; 2503229Spst}; 2513229Spst 2523229Spst 2533229Spststatic const char* 2543229Spstget_leaf(const char* signature) 2553229Spst{ 2563229Spst if (signature == NULL) 2573229Spst return NULL; 2583229Spst 2593229Spst const char* separator = strrchr(signature, '/'); 2603229Spst if (separator != NULL) 2613229Spst return separator + 1; 2623229Spst 2633229Spst return signature; 2643229Spst} 2653229Spst 2663229Spst 2673229Spst// #pragma mark - 2683229Spst 2693229Spst 2703229SpstSession::Session(uid_t user, const BMessenger& daemon) 2713229Spst : 2723229Spst fUser(user), 2733229Spst fDaemon(daemon) 2743229Spst{ 2753229Spst} 2763229Spst 2773229Spst 2783229Spst// #pragma mark - 2793229Spst 2803229Spst 2813229SpstExternalEventSource::ExternalEventSource(BMessenger& source, 2823229Spst const char* ownerName, const char* name, uint32 flags) 28313572Spst : 2843229Spst fSource(source), 2853229Spst fName(name), 2863229Spst fOwnerName(ownerName), 2873229Spst fFlags(flags), 2883229Spst fDestinations(5), 2893229Spst fStickyTriggered(false) 2903229Spst{ 2913229Spst} 2923229Spst 2933229Spst 2943229SpstExternalEventSource::~ExternalEventSource() 2953229Spst{ 2963229Spst} 2973229Spst 2983229Spst 2993229Spstconst char* 3003229SpstExternalEventSource::Name() const 3013229Spst{ 3023229Spst return fName.String(); 3033229Spst} 3043229Spst 3053229Spst 3063229Spstconst char* 3073229SpstExternalEventSource::OwnerName() const 3083229Spst{ 3093229Spst return fOwnerName.String(); 3103229Spst} 3113229Spst 3123229Spst 313228584Sdimvoid 3143229SpstExternalEventSource::Trigger() 3153229Spst{ 3163229Spst for (int32 index = 0; index < fDestinations.CountItems(); index++) 3173229Spst Events::TriggerExternalEvent(fDestinations.ItemAt(index)); 3183229Spst 3193229Spst if ((fFlags & B_STICKY_EVENT) != 0) 3203229Spst fStickyTriggered = true; 3213229Spst} 3223229Spst 3233229Spst 3243229Spstvoid 3253229SpstExternalEventSource::ResetSticky() 3263229Spst{ 3273229Spst if ((fFlags & B_STICKY_EVENT) != 0) 3283229Spst fStickyTriggered = false; 3293229Spst 3303229Spst for (int32 index = 0; index < fDestinations.CountItems(); index++) 3313229Spst Events::ResetStickyExternalEvent(fDestinations.ItemAt(index)); 3323229Spst} 3333229Spst 3343229Spst 3353229Spststatus_t 3363229SpstExternalEventSource::AddDestination(Event* event) 3373229Spst{ 3383229Spst if (fStickyTriggered) 33984125Siedowse Events::TriggerExternalEvent(event); 3403229Spst 3413229Spst if (fDestinations.AddItem(event)) 3423229Spst return B_OK; 3433229Spst 3443229Spst return B_NO_MEMORY; 3453229Spst} 3463229Spst 3473229Spst 3483229Spstvoid 3493229SpstExternalEventSource::RemoveDestination(Event* event) 3503229Spst{ 3513229Spst fDestinations.RemoveItem(event); 3523229Spst} 3533229Spst 3543229Spst 3553229Spst// #pragma mark - 3563229Spst 3573229Spst 3583229SpstLaunchDaemon::LaunchDaemon(bool userMode, status_t& error) 3593229Spst : 3603229Spst BServer(kLaunchDaemonSignature, NULL, 3613229Spst create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, 3623229Spst userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error), 3633229Spst fInitTarget(userMode ? NULL : new Target("init")), 3643229Spst#ifdef TEST_MODE 3653229Spst fUserMode(true) 3663229Spst#else 3673229Spst fUserMode(userMode) 3683229Spst#endif 3693229Spst{ 3703229Spst mutex_init(&fTeamsLock, "teams lock"); 3713229Spst 3723229Spst fMainWorker = new MainWorker(fJobQueue); 3733229Spst fMainWorker->Init(); 3743229Spst 3753229Spst if (fInitTarget != NULL) 3763229Spst _AddTarget(fInitTarget); 3773229Spst 3783229Spst // We may not be able to talk to the registrar 3793229Spst if (!fUserMode) 3803229Spst BRoster::Private().SetWithoutRegistrar(true); 3813229Spst} 3823229Spst 3833229Spst 3843229SpstLaunchDaemon::~LaunchDaemon() 3853229Spst{ 3863229Spst} 3873229Spst 3883229Spst 3893229SpstJob* 3903229SpstLaunchDaemon::FindJob(const char* name) const 3913229Spst{ 3923229Spst if (name == NULL) 3933229Spst return NULL; 3943229Spst 3953229Spst JobMap::const_iterator found = fJobs.find(BString(name).ToLower()); 3963229Spst if (found != fJobs.end()) 3973229Spst return found->second; 3983229Spst 3993229Spst return NULL; 4003229Spst} 4013229Spst 4023229Spst 4033229SpstTarget* 4043229SpstLaunchDaemon::FindTarget(const char* name) const 4053229Spst{ 4063229Spst if (name == NULL) 4073229Spst return NULL; 4083229Spst 4093229Spst TargetMap::const_iterator found = fTargets.find(BString(name).ToLower()); 4103229Spst if (found != fTargets.end()) 4113229Spst return found->second; 4123229Spst 4133229Spst return NULL; 4143229Spst} 4153229Spst 4163229Spst 4173229SpstSession* 4183229SpstLaunchDaemon::FindSession(uid_t user) const 4193229Spst{ 4203229Spst SessionMap::const_iterator found = fSessions.find(user); 4213229Spst if (found != fSessions.end()) 4223229Spst return found->second; 4233229Spst 4243229Spst return NULL; 4253229Spst} 4263229Spst 4273229Spst 4283229Spstbool 4293229SpstLaunchDaemon::IsSafeMode() const 4303229Spst{ 4313229Spst return fSafeMode; 4323229Spst} 4333229Spst 4343229Spst 4353229Spstbool 4363229SpstLaunchDaemon::BootVolumeIsReadOnly() const 4373229Spst{ 4383229Spst return fReadOnlyBootVolume; 4393229Spst} 4403229Spst 4413229Spst 4423229Spststatus_t 4433229SpstLaunchDaemon::RegisterExternalEvent(Event* event, const char* name, 4443229Spst const BStringList& arguments) 4453229Spst{ 4463229Spst status_t status TRACE_ONLY = B_NAME_NOT_FOUND; 4473229Spst for (EventMap::iterator iterator = fEvents.begin(); 4483229Spst iterator != fEvents.end(); iterator++) { 4493229Spst ExternalEventSource* eventSource = iterator->second; 4503229Spst Event* externalEvent = Events::ResolveExternalEvent(event, 4513229Spst eventSource->Name(), eventSource->Flags()); 4523229Spst if (externalEvent != NULL) { 4533229Spst status = eventSource->AddDestination(event); 4543229Spst break; 4553229Spst } 4563229Spst } 4573229Spst 4583229Spst TRACE("Register external event '%s': %" B_PRId32 "\n", name, status); 4593229Spst 4603229Spst // Even if we failed to find a matching source, we do not want to return an error, 4613229Spst // as that will be propagated up the chain and prevent this job from being instantiated. 4623229Spst // Jobs will be re-scanned later for unregistered external events. 4633229Spst return B_OK; 4643229Spst} 4653229Spst 4663229Spst 4673229Spstvoid 4683229SpstLaunchDaemon::UnregisterExternalEvent(Event* event, const char* name) 4693229Spst{ 4703229Spst for (EventMap::iterator iterator = fEvents.begin(); 4713229Spst iterator != fEvents.end(); iterator++) { 4723229Spst ExternalEventSource* eventSource = iterator->second; 4733229Spst Event* externalEvent = Events::ResolveExternalEvent(event, 4743229Spst eventSource->Name(), eventSource->Flags()); 4753229Spst if (externalEvent != NULL) { 4763229Spst eventSource->RemoveDestination(event); 4773229Spst break; 4783229Spst } 4793229Spst } 4803229Spst} 4813229Spst 4823229Spst 4833229Spstvoid 4843229SpstLaunchDaemon::TeamLaunched(Job* job, status_t status) 4853229Spst{ 4863229Spst fLog.JobLaunched(job, status); 4873229Spst 4883229Spst MutexLocker locker(fTeamsLock); 4893229Spst fTeams.insert(std::make_pair(job->Team(), job)); 4903229Spst} 4913229Spst 4923229Spst 493void 494LaunchDaemon::ReadyToRun() 495{ 496 _RetrieveKernelOptions(); 497 _SetupEnvironment(); 498 499 fReadOnlyBootVolume = Utility::IsReadOnlyVolume("/boot"); 500 if (fReadOnlyBootVolume) 501 Utility::BlockMedia("/boot", true); 502 503 if (fUserMode) { 504#ifndef TEST_MODE 505 BLaunchRoster roster; 506 BLaunchRoster::Private(roster).RegisterSessionDaemon(this); 507#endif // TEST_MODE 508 } else 509 _InitSystem(); 510 511 BStringList paths; 512#ifdef TEST_MODE 513 paths.Add("/boot/home/test_launch"); 514#else 515 if (fUserMode) { 516 // System-wide user specific jobs 517 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kUserLaunchDirectory, 518 B_FIND_PATHS_SYSTEM_ONLY, paths); 519 _ReadPaths(paths); 520 } 521 522 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory, 523 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 524 _ReadPaths(paths); 525 526 if (fUserMode) { 527 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, 528 kUserLaunchDirectory, B_FIND_PATHS_SYSTEM_ONLY, paths); 529 _ReadPaths(paths); 530 } 531 532 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory, 533 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 534#endif // TEST_MODE 535 _ReadPaths(paths); 536 537 BMessenger target(this); 538 BMessenger::Private messengerPrivate(target); 539 port_id port = messengerPrivate.Port(); 540 int32 token = messengerPrivate.Token(); 541 __start_watching_system(-1, B_WATCH_SYSTEM_TEAM_DELETION, port, token); 542 543 _InitJobs(NULL); 544 _LaunchJobs(NULL); 545 546 // Launch run targets (ignores events) 547 for (int32 index = 0; index < fRunTargets.CountStrings(); index++) { 548 Target* target = FindTarget(fRunTargets.StringAt(index)); 549 if (target != NULL) 550 _LaunchJobs(target); 551 } 552 553 if (fUserMode) 554 be_roster->StartWatching(this, B_REQUEST_LAUNCHED); 555} 556 557 558void 559LaunchDaemon::MessageReceived(BMessage* message) 560{ 561 switch (message->what) { 562 case B_SYSTEM_OBJECT_UPDATE: 563 { 564 int32 opcode = message->GetInt32("opcode", 0); 565 team_id team = (team_id)message->GetInt32("team", -1); 566 if (opcode != B_TEAM_DELETED || team < 0) 567 break; 568 569 MutexLocker locker(fTeamsLock); 570 571 TeamMap::iterator found = fTeams.find(team); 572 if (found != fTeams.end()) { 573 Job* job = found->second; 574 TRACE("Job %s ended!\n", job->Name()); 575 576 // Get the exit status, and pass it on to the log 577 status_t exitStatus = B_OK; 578 wait_for_thread(team, &exitStatus); 579 fLog.JobTerminated(job, exitStatus); 580 job->TeamDeleted(); 581 582 if (job->IsService()) { 583 bool inProgress = false; 584 BRoster roster; 585 BRoster::Private rosterPrivate(roster); 586 status_t status = rosterPrivate.IsShutDownInProgress( 587 &inProgress); 588 if (status != B_OK || !inProgress) { 589 // TODO: take restart throttle into account 590 _LaunchJob(job); 591 } 592 } 593 } 594 break; 595 } 596 case B_SOME_APP_LAUNCHED: 597 { 598 team_id team = (team_id)message->GetInt32("be:team", -1); 599 Job* job = NULL; 600 601 MutexLocker locker(fTeamsLock); 602 603 TeamMap::iterator found = fTeams.find(team); 604 if (found != fTeams.end()) { 605 job = found->second; 606 locker.Unlock(); 607 } else { 608 locker.Unlock(); 609 610 // Find job by name instead 611 const char* signature = message->GetString("be:signature"); 612 job = FindJob(get_leaf(signature)); 613 if (job != NULL) { 614 TRACE("Updated default port of untracked team %d, %s\n", 615 (int)team, signature); 616 } 617 } 618 619 if (job != NULL) { 620 // Update port info 621 app_info info; 622 status_t status = be_roster->GetRunningAppInfo(team, &info); 623 if (status == B_OK && info.port != job->DefaultPort()) { 624 TRACE("Update default port for %s to %d\n", job->Name(), 625 (int)info.port); 626 job->SetDefaultPort(info.port); 627 } 628 } 629 break; 630 } 631 632 case B_GET_LAUNCH_DATA: 633 _HandleGetLaunchData(message); 634 break; 635 636 case B_LAUNCH_TARGET: 637 _HandleLaunchTarget(message); 638 break; 639 case B_STOP_LAUNCH_TARGET: 640 _HandleStopLaunchTarget(message); 641 break; 642 case B_LAUNCH_JOB: 643 _HandleLaunchJob(message); 644 break; 645 case B_ENABLE_LAUNCH_JOB: 646 _HandleEnableLaunchJob(message); 647 break; 648 case B_STOP_LAUNCH_JOB: 649 _HandleStopLaunchJob(message); 650 break; 651 652 case B_LAUNCH_SESSION: 653 _HandleLaunchSession(message); 654 break; 655 case B_REGISTER_SESSION_DAEMON: 656 _HandleRegisterSessionDaemon(message); 657 break; 658 659 case B_REGISTER_LAUNCH_EVENT: 660 _HandleRegisterLaunchEvent(message); 661 break; 662 case B_UNREGISTER_LAUNCH_EVENT: 663 _HandleUnregisterLaunchEvent(message); 664 break; 665 case B_NOTIFY_LAUNCH_EVENT: 666 _HandleNotifyLaunchEvent(message); 667 break; 668 case B_RESET_STICKY_LAUNCH_EVENT: 669 _HandleResetStickyLaunchEvent(message); 670 break; 671 672 case B_GET_LAUNCH_TARGETS: 673 _HandleGetLaunchTargets(message); 674 break; 675 case B_GET_LAUNCH_TARGET_INFO: 676 _HandleGetLaunchTargetInfo(message); 677 break; 678 case B_GET_LAUNCH_JOBS: 679 _HandleGetLaunchJobs(message); 680 break; 681 case B_GET_LAUNCH_JOB_INFO: 682 _HandleGetLaunchJobInfo(message); 683 break; 684 685 case B_GET_LAUNCH_LOG: 686 _HandleGetLaunchLog(message); 687 break; 688 689 case kMsgEventTriggered: 690 { 691 // An internal event has been triggered. 692 // Check if its job(s) can be launched now. 693 const char* name = message->GetString("owner"); 694 if (name == NULL) 695 break; 696 697 Event* event = (Event*)message->GetPointer("event"); 698 699 Job* job = FindJob(name); 700 if (job != NULL) { 701 fLog.EventTriggered(job, event); 702 _LaunchJob(job); 703 break; 704 } 705 706 Target* target = FindTarget(name); 707 if (target != NULL) { 708 fLog.EventTriggered(target, event); 709 _LaunchJobs(target); 710 break; 711 } 712 break; 713 } 714 715 default: 716 BServer::MessageReceived(message); 717 break; 718 } 719} 720 721 722void 723LaunchDaemon::_HandleGetLaunchData(BMessage* message) 724{ 725 uid_t user = _GetUserID(message); 726 if (user < 0) 727 return; 728 729 BMessage reply((uint32)B_OK); 730 bool launchJob = true; 731 732 Job* job = FindJob(get_leaf(message->GetString("name"))); 733 if (job == NULL) { 734 Session* session = FindSession(user); 735 if (session != NULL) { 736 // Forward request to user launch_daemon 737 if (session->Daemon().SendMessage(message) == B_OK) 738 return; 739 } 740 reply.what = B_NAME_NOT_FOUND; 741 } else if (job->IsService() && !job->IsLaunched()) { 742 if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) { 743 // The job exists, but cannot be started yet, as its 744 // conditions are not met; don't make it available yet 745 // TODO: we may not want to initialize jobs with conditions 746 // that aren't met yet 747 reply.what = B_NO_INIT; 748 } else if (job->Event() != NULL) { 749 if (!Events::TriggerDemand(job->Event())) { 750 // The job is not triggered by demand; we cannot start it now 751 reply.what = B_NO_INIT; 752 } else { 753 // The job has already been triggered, don't launch it again 754 launchJob = false; 755 } 756 } 757 } else 758 launchJob = false; 759 760 bool ownsMessage = false; 761 if (reply.what == B_OK) { 762 // Launch the job if it hasn't been launched already 763 if (launchJob) 764 _LaunchJob(job, TRIGGER_DEMAND); 765 766 DetachCurrentMessage(); 767 status_t result = job->HandleGetLaunchData(message); 768 if (result == B_OK) { 769 // Replying is delegated to the job. 770 return; 771 } 772 773 ownsMessage = true; 774 reply.what = result; 775 } 776 777 message->SendReply(&reply); 778 if (ownsMessage) 779 delete message; 780} 781 782 783void 784LaunchDaemon::_HandleLaunchTarget(BMessage* message) 785{ 786 uid_t user = _GetUserID(message); 787 if (user < 0) 788 return; 789 790 const char* name = message->GetString("target"); 791 const char* baseName = message->GetString("base target"); 792 793 Target* target = FindTarget(name); 794 if (target == NULL && baseName != NULL) { 795 Target* baseTarget = FindTarget(baseName); 796 if (baseTarget != NULL) { 797 target = new Target(name); 798 799 // Copy all jobs with the base target into the new target 800 for (JobMap::iterator iterator = fJobs.begin(); 801 iterator != fJobs.end();) { 802 Job* job = iterator->second; 803 iterator++; 804 805 if (job->Target() == baseTarget) { 806 Job* copy = new Job(*job); 807 copy->SetTarget(target); 808 809 fJobs.insert(std::make_pair(copy->Name(), copy)); 810 } 811 } 812 } 813 } 814 if (target == NULL) { 815 Session* session = FindSession(user); 816 if (session != NULL) { 817 // Forward request to user launch_daemon 818 if (session->Daemon().SendMessage(message) == B_OK) 819 return; 820 } 821 822 BMessage reply(B_NAME_NOT_FOUND); 823 message->SendReply(&reply); 824 return; 825 } 826 827 BMessage data; 828 if (message->FindMessage("data", &data) == B_OK) 829 target->AddData(data.GetString("name"), data); 830 831 _LaunchJobs(target); 832 833 BMessage reply((uint32)B_OK); 834 message->SendReply(&reply); 835} 836 837 838void 839LaunchDaemon::_HandleStopLaunchTarget(BMessage* message) 840{ 841 uid_t user = _GetUserID(message); 842 if (user < 0) 843 return; 844 845 const char* name = message->GetString("target"); 846 847 Target* target = FindTarget(name); 848 if (target == NULL) { 849 Session* session = FindSession(user); 850 if (session != NULL) { 851 // Forward request to user launch_daemon 852 if (session->Daemon().SendMessage(message) == B_OK) 853 return; 854 } 855 856 BMessage reply(B_NAME_NOT_FOUND); 857 message->SendReply(&reply); 858 return; 859 } 860 861 BMessage data; 862 if (message->FindMessage("data", &data) == B_OK) 863 target->AddData(data.GetString("name"), data); 864 865 bool force = message->GetBool("force"); 866 fLog.JobStopped(target, force); 867 _StopJobs(target, force); 868 869 BMessage reply((uint32)B_OK); 870 message->SendReply(&reply); 871} 872 873 874void 875LaunchDaemon::_HandleLaunchJob(BMessage* message) 876{ 877 uid_t user = _GetUserID(message); 878 if (user < 0) 879 return; 880 881 const char* name = message->GetString("name"); 882 883 Job* job = FindJob(name); 884 if (job == NULL) { 885 Session* session = FindSession(user); 886 if (session != NULL) { 887 // Forward request to user launch_daemon 888 if (session->Daemon().SendMessage(message) == B_OK) 889 return; 890 } 891 892 BMessage reply(B_NAME_NOT_FOUND); 893 message->SendReply(&reply); 894 return; 895 } 896 897 job->SetEnabled(true); 898 _LaunchJob(job, FORCE_NOW); 899 900 BMessage reply((uint32)B_OK); 901 message->SendReply(&reply); 902} 903 904 905void 906LaunchDaemon::_HandleEnableLaunchJob(BMessage* message) 907{ 908 uid_t user = _GetUserID(message); 909 if (user < 0) 910 return; 911 912 const char* name = message->GetString("name"); 913 bool enable = message->GetBool("enable"); 914 915 Job* job = FindJob(name); 916 if (job == NULL) { 917 Session* session = FindSession(user); 918 if (session != NULL) { 919 // Forward request to user launch_daemon 920 if (session->Daemon().SendMessage(message) == B_OK) 921 return; 922 } 923 924 BMessage reply(B_NAME_NOT_FOUND); 925 message->SendReply(&reply); 926 return; 927 } 928 929 job->SetEnabled(enable); 930 fLog.JobEnabled(job, enable); 931 932 BMessage reply((uint32)B_OK); 933 message->SendReply(&reply); 934} 935 936 937void 938LaunchDaemon::_HandleStopLaunchJob(BMessage* message) 939{ 940 uid_t user = _GetUserID(message); 941 if (user < 0) 942 return; 943 944 const char* name = message->GetString("name"); 945 946 Job* job = FindJob(name); 947 if (job == NULL) { 948 Session* session = FindSession(user); 949 if (session != NULL) { 950 // Forward request to user launch_daemon 951 if (session->Daemon().SendMessage(message) == B_OK) 952 return; 953 } 954 955 BMessage reply(B_NAME_NOT_FOUND); 956 message->SendReply(&reply); 957 return; 958 } 959 960 bool force = message->GetBool("force"); 961 fLog.JobStopped(job, force); 962 _StopJob(job, force); 963 964 BMessage reply((uint32)B_OK); 965 message->SendReply(&reply); 966} 967 968 969void 970LaunchDaemon::_HandleLaunchSession(BMessage* message) 971{ 972 uid_t user = _GetUserID(message); 973 if (user < 0) 974 return; 975 976 status_t status = B_OK; 977 const char* login = message->GetString("login"); 978 if (login == NULL) 979 status = B_BAD_VALUE; 980 if (status == B_OK && user != 0) { 981 // Only the root user can start sessions 982 // TODO: we'd actually need to know the uid of the sender 983 status = B_PERMISSION_DENIED; 984 } 985 if (status == B_OK) 986 status = _StartSession(login); 987 988 BMessage reply((uint32)status); 989 message->SendReply(&reply); 990} 991 992 993void 994LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message) 995{ 996 uid_t user = _GetUserID(message); 997 if (user < 0) 998 return; 999 1000 status_t status = B_OK; 1001 1002 BMessenger target; 1003 if (message->FindMessenger("daemon", &target) != B_OK) 1004 status = B_BAD_VALUE; 1005 1006 if (status == B_OK) { 1007 Session* session = new (std::nothrow) Session(user, target); 1008 if (session != NULL) 1009 fSessions.insert(std::make_pair(user, session)); 1010 else 1011 status = B_NO_MEMORY; 1012 1013 // Send registration messages for all already-known events. 1014 for (EventMap::iterator iterator = fEvents.begin(); iterator != fEvents.end(); 1015 iterator++) { 1016 ExternalEventSource* eventSource = iterator->second; 1017 if (eventSource->Name() != iterator->first) 1018 continue; // skip alternative event names 1019 1020 BMessage message(B_REGISTER_LAUNCH_EVENT); 1021 message.AddInt32("user", 0); 1022 message.AddString("name", eventSource->Name()); 1023 message.AddString("owner", eventSource->OwnerName()); 1024 message.AddUInt32("flags", eventSource->Flags()); 1025 message.AddMessenger("source", eventSource->Source()); 1026 target.SendMessage(&message); 1027 1028 if (eventSource->StickyTriggered()) { 1029 message.what = B_NOTIFY_LAUNCH_EVENT; 1030 target.SendMessage(&message); 1031 } 1032 } 1033 } 1034 1035 BMessage reply((uint32)status); 1036 message->SendReply(&reply); 1037} 1038 1039 1040void 1041LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message) 1042{ 1043 uid_t user = _GetUserID(message); 1044 if (user < 0) 1045 return; 1046 1047 if (user == 0 || fUserMode) { 1048 status_t status = B_OK; 1049 1050 const char* name = message->GetString("name"); 1051 const char* ownerName = message->GetString("owner"); 1052 uint32 flags = message->GetUInt32("flags", 0); 1053 BMessenger source; 1054 if (name != NULL && ownerName != NULL 1055 && message->FindMessenger("source", &source) == B_OK) { 1056 // Register event 1057 ownerName = get_leaf(ownerName); 1058 1059 ExternalEventSource* event = new (std::nothrow) 1060 ExternalEventSource(source, ownerName, name, flags); 1061 if (event != NULL) { 1062 // Use short name, and fully qualified name 1063 BString eventName = name; 1064 fEvents.insert(std::make_pair(eventName, event)); 1065 _ResolveExternalEvents(event, eventName); 1066 1067 eventName.Prepend("/"); 1068 eventName.Prepend(ownerName); 1069 fEvents.insert(std::make_pair(eventName, event)); 1070 _ResolveExternalEvents(event, eventName); 1071 1072 fLog.ExternalEventRegistered(name); 1073 } else 1074 status = B_NO_MEMORY; 1075 } else 1076 status = B_BAD_VALUE; 1077 1078 BMessage reply((uint32)status); 1079 message->SendReply(&reply); 1080 } 1081 1082 _ForwardEventMessage(user, message); 1083} 1084 1085 1086void 1087LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message) 1088{ 1089 uid_t user = _GetUserID(message); 1090 if (user < 0) 1091 return; 1092 1093 if (user == 0 || fUserMode) { 1094 status_t status = B_OK; 1095 1096 const char* name = message->GetString("name"); 1097 const char* ownerName = message->GetString("owner"); 1098 BMessenger source; 1099 if (name != NULL && ownerName != NULL 1100 && message->FindMessenger("source", &source) == B_OK) { 1101 // Unregister short and fully qualified event name 1102 ownerName = get_leaf(ownerName); 1103 1104 BString eventName = name; 1105 fEvents.erase(eventName); 1106 1107 eventName.Prepend("/"); 1108 eventName.Prepend(ownerName); 1109 fEvents.erase(eventName); 1110 1111 fLog.ExternalEventRegistered(name); 1112 } else 1113 status = B_BAD_VALUE; 1114 1115 BMessage reply((uint32)status); 1116 message->SendReply(&reply); 1117 } 1118 1119 _ForwardEventMessage(user, message); 1120} 1121 1122 1123void 1124LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message) 1125{ 1126 uid_t user = _GetUserID(message); 1127 if (user < 0) 1128 return; 1129 1130 if (user == 0 || fUserMode) { 1131 // Trigger events 1132 const char* name = message->GetString("name"); 1133 const char* ownerName = message->GetString("owner"); 1134 // TODO: support arguments (as selectors) 1135 1136 ExternalEventSource* event = _FindExternalEventSource(ownerName, name); 1137 if (event != NULL) { 1138 fLog.ExternalEventTriggered(name); 1139 event->Trigger(); 1140 } 1141 } 1142 1143 _ForwardEventMessage(user, message); 1144} 1145 1146 1147void 1148LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message) 1149{ 1150 uid_t user = _GetUserID(message); 1151 if (user < 0) 1152 return; 1153 1154 if (user == 0 || fUserMode) { 1155 // Reset sticky events 1156 const char* name = message->GetString("name"); 1157 const char* ownerName = message->GetString("owner"); 1158 // TODO: support arguments (as selectors) 1159 1160 ExternalEventSource* eventSource = _FindExternalEventSource(ownerName, name); 1161 if (eventSource != NULL) 1162 eventSource->ResetSticky(); 1163 } 1164 1165 _ForwardEventMessage(user, message); 1166} 1167 1168 1169void 1170LaunchDaemon::_HandleGetLaunchTargets(BMessage* message) 1171{ 1172 uid_t user = _GetUserID(message); 1173 if (user < 0) 1174 return; 1175 1176 BMessage reply; 1177 status_t status = B_OK; 1178 1179 if (!fUserMode) { 1180 // Request the data from the user's daemon, too 1181 Session* session = FindSession(user); 1182 if (session != NULL) { 1183 BMessage request(B_GET_LAUNCH_TARGETS); 1184 status = request.AddInt32("user", 0); 1185 if (status == B_OK) { 1186 status = session->Daemon().SendMessage(&request, 1187 &reply); 1188 } 1189 if (status == B_OK) 1190 status = reply.what; 1191 } else 1192 status = B_NAME_NOT_FOUND; 1193 } 1194 1195 if (status == B_OK) { 1196 TargetMap::const_iterator iterator = fTargets.begin(); 1197 for (; iterator != fTargets.end(); iterator++) 1198 reply.AddString("target", iterator->first); 1199 } 1200 1201 reply.what = status; 1202 message->SendReply(&reply); 1203} 1204 1205 1206void 1207LaunchDaemon::_HandleGetLaunchTargetInfo(BMessage* message) 1208{ 1209 uid_t user = _GetUserID(message); 1210 if (user < 0) 1211 return; 1212 1213 const char* name = message->GetString("name"); 1214 Target* target = FindTarget(name); 1215 if (target == NULL && !fUserMode) { 1216 _ForwardEventMessage(user, message); 1217 return; 1218 } 1219 1220 BMessage info(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND)); 1221 if (target != NULL) { 1222 _GetBaseJobInfo(target, info); 1223 1224 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1225 iterator++) { 1226 Job* job = iterator->second; 1227 if (job->Target() == target) 1228 info.AddString("job", job->Name()); 1229 } 1230 } 1231 message->SendReply(&info); 1232} 1233 1234 1235void 1236LaunchDaemon::_HandleGetLaunchJobs(BMessage* message) 1237{ 1238 uid_t user = _GetUserID(message); 1239 if (user < 0) 1240 return; 1241 1242 const char* targetName = message->GetString("target"); 1243 1244 BMessage reply; 1245 status_t status = B_OK; 1246 1247 if (!fUserMode) { 1248 // Request the data from the user's daemon, too 1249 Session* session = FindSession(user); 1250 if (session != NULL) { 1251 BMessage request(B_GET_LAUNCH_JOBS); 1252 status = request.AddInt32("user", 0); 1253 if (status == B_OK && targetName != NULL) 1254 status = request.AddString("target", targetName); 1255 if (status == B_OK) { 1256 status = session->Daemon().SendMessage(&request, 1257 &reply); 1258 } 1259 if (status == B_OK) 1260 status = reply.what; 1261 } else 1262 status = B_NAME_NOT_FOUND; 1263 } 1264 1265 if (status == B_OK) { 1266 JobMap::const_iterator iterator = fJobs.begin(); 1267 for (; iterator != fJobs.end(); iterator++) { 1268 Job* job = iterator->second; 1269 if (targetName != NULL && (job->Target() == NULL 1270 || job->Target()->Title() != targetName)) { 1271 continue; 1272 } 1273 reply.AddString("job", iterator->first); 1274 } 1275 } 1276 1277 reply.what = status; 1278 message->SendReply(&reply); 1279} 1280 1281 1282void 1283LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message) 1284{ 1285 uid_t user = _GetUserID(message); 1286 if (user < 0) 1287 return; 1288 1289 const char* name = message->GetString("name"); 1290 Job* job = FindJob(name); 1291 if (job == NULL && !fUserMode) { 1292 _ForwardEventMessage(user, message); 1293 return; 1294 } 1295 1296 BMessage info(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND)); 1297 if (job != NULL) { 1298 _GetBaseJobInfo(job, info); 1299 1300 info.SetInt32("team", job->Team()); 1301 info.SetBool("enabled", job->IsEnabled()); 1302 info.SetBool("running", job->IsRunning()); 1303 info.SetBool("launched", job->IsLaunched()); 1304 info.SetBool("service", job->IsService()); 1305 1306 if (job->Target() != NULL) 1307 info.SetString("target", job->Target()->Name()); 1308 1309 for (int32 i = 0; i < job->Arguments().CountStrings(); i++) 1310 info.AddString("launch", job->Arguments().StringAt(i)); 1311 1312 for (int32 i = 0; i < job->Requirements().CountStrings(); i++) 1313 info.AddString("requires", job->Requirements().StringAt(i)); 1314 1315 PortMap::const_iterator iterator = job->Ports().begin(); 1316 for (; iterator != job->Ports().end(); iterator++) 1317 info.AddMessage("port", &iterator->second); 1318 } 1319 message->SendReply(&info); 1320} 1321 1322 1323void 1324LaunchDaemon::_HandleGetLaunchLog(BMessage* message) 1325{ 1326 uid_t user = _GetUserID(message); 1327 if (user < 0) 1328 return; 1329 1330 BMessage filter; 1331 BString jobName; 1332 const char* event = NULL; 1333 int32 limit = 0; 1334 bool systemOnly = false; 1335 bool userOnly = false; 1336 if (message->FindMessage("filter", &filter) == B_OK) { 1337 limit = filter.GetInt32("limit", 0); 1338 jobName = filter.GetString("job"); 1339 jobName.ToLower(); 1340 event = filter.GetString("event"); 1341 systemOnly = filter.GetBool("systemOnly"); 1342 userOnly = filter.GetBool("userOnly"); 1343 } 1344 1345 BMessage info((uint32)B_OK); 1346 int32 count = 0; 1347 1348 if (user == 0 || !userOnly) { 1349 LogItemList::Iterator iterator = fLog.Iterator(); 1350 while (iterator.HasNext()) { 1351 LogItem* item = iterator.Next(); 1352 if (!item->Matches(jobName.IsEmpty() ? NULL : jobName.String(), 1353 event)) { 1354 continue; 1355 } 1356 1357 BMessage itemMessage; 1358 itemMessage.AddUInt64("when", item->When()); 1359 itemMessage.AddInt32("type", (int32)item->Type()); 1360 itemMessage.AddString("message", item->Message()); 1361 1362 BMessage parameter; 1363 item->GetParameter(parameter); 1364 itemMessage.AddMessage("parameter", ¶meter); 1365 1366 info.AddMessage("item", &itemMessage); 1367 1368 // limit == 0 means no limit 1369 if (++count == limit) 1370 break; 1371 } 1372 } 1373 1374 // Get the list from the user daemon, and merge it into our reply 1375 Session* session = FindSession(user); 1376 if (session != NULL && !systemOnly) { 1377 if (limit != 0) { 1378 // Update limit for user daemon 1379 limit -= count; 1380 if (limit <= 0) { 1381 message->SendReply(&info); 1382 return; 1383 } 1384 } 1385 1386 BMessage reply; 1387 1388 BMessage request(B_GET_LAUNCH_LOG); 1389 status_t status = request.AddInt32("user", 0); 1390 if (status == B_OK && (limit != 0 || !jobName.IsEmpty() 1391 || event != NULL)) { 1392 // Forward filter specification when needed 1393 status = filter.SetInt32("limit", limit); 1394 if (status == B_OK) 1395 status = request.AddMessage("filter", &filter); 1396 } 1397 if (status == B_OK) 1398 status = session->Daemon().SendMessage(&request, &reply); 1399 if (status == B_OK) 1400 info.AddMessage("user", &reply); 1401 } 1402 1403 message->SendReply(&info); 1404} 1405 1406 1407uid_t 1408LaunchDaemon::_GetUserID(BMessage* message) 1409{ 1410 uid_t user = (uid_t)message->GetInt32("user", -1); 1411 if (user < 0) { 1412 BMessage reply((uint32)B_BAD_VALUE); 1413 message->SendReply(&reply); 1414 } 1415 return user; 1416} 1417 1418 1419void 1420LaunchDaemon::_ReadPaths(const BStringList& paths) 1421{ 1422 for (int32 i = 0; i < paths.CountStrings(); i++) { 1423 BEntry entry(paths.StringAt(i)); 1424 if (entry.InitCheck() != B_OK || !entry.Exists()) 1425 continue; 1426 1427 _ReadDirectory(NULL, entry); 1428 } 1429} 1430 1431 1432void 1433LaunchDaemon::_ReadEntry(const char* context, BEntry& entry) 1434{ 1435 if (entry.IsDirectory()) 1436 _ReadDirectory(context, entry); 1437 else 1438 _ReadFile(context, entry); 1439} 1440 1441 1442void 1443LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry) 1444{ 1445 BDirectory directory(&directoryEntry); 1446 1447 BEntry entry; 1448 while (directory.GetNextEntry(&entry) == B_OK) { 1449 _ReadEntry(context, entry); 1450 } 1451} 1452 1453 1454status_t 1455LaunchDaemon::_ReadFile(const char* context, BEntry& entry) 1456{ 1457 BPath path; 1458 status_t status = path.SetTo(&entry); 1459 if (status != B_OK) 1460 return status; 1461 1462 SettingsParser parser; 1463 BMessage message; 1464 status = parser.ParseFile(path.Path(), message); 1465 if (status == B_OK) { 1466 TRACE("launch_daemon: read file %s\n", path.Path()); 1467 _AddJobs(NULL, message); 1468 _AddTargets(message); 1469 _AddRunTargets(message); 1470 } 1471 1472 return status; 1473} 1474 1475 1476void 1477LaunchDaemon::_AddJobs(Target* target, BMessage& message) 1478{ 1479 BMessage job; 1480 for (int32 index = 0; message.FindMessage("service", index, 1481 &job) == B_OK; index++) { 1482 _AddJob(target, true, job); 1483 } 1484 1485 for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK; 1486 index++) { 1487 _AddJob(target, false, job); 1488 } 1489} 1490 1491 1492void 1493LaunchDaemon::_AddTargets(BMessage& message) 1494{ 1495 BMessage targetMessage; 1496 for (int32 index = 0; message.FindMessage("target", index, 1497 &targetMessage) == B_OK; index++) { 1498 const char* name = targetMessage.GetString("name"); 1499 if (name == NULL) { 1500 // TODO: log error 1501 debug_printf("Target has no name, ignoring it!\n"); 1502 continue; 1503 } 1504 1505 Target* target = FindTarget(name); 1506 if (target == NULL) { 1507 target = new Target(name); 1508 _AddTarget(target); 1509 } else if (targetMessage.GetBool("reset")) { 1510 // Remove all jobs from this target 1511 for (JobMap::iterator iterator = fJobs.begin(); 1512 iterator != fJobs.end();) { 1513 Job* job = iterator->second; 1514 JobMap::iterator remove = iterator++; 1515 1516 if (job->Target() == target) { 1517 fJobs.erase(remove); 1518 delete job; 1519 } 1520 } 1521 } 1522 1523 _SetCondition(target, targetMessage); 1524 _SetEvent(target, targetMessage); 1525 _SetEnvironment(target, targetMessage); 1526 _AddJobs(target, targetMessage); 1527 1528 if (target->Event() != NULL) 1529 target->Event()->Register(*this); 1530 } 1531} 1532 1533 1534void 1535LaunchDaemon::_AddRunTargets(BMessage& message) 1536{ 1537 BMessage runMessage; 1538 for (int32 index = 0; message.FindMessage("run", index, 1539 &runMessage) == B_OK; index++) { 1540 BMessage conditions; 1541 bool pass = true; 1542 if (runMessage.FindMessage("if", &conditions) == B_OK) { 1543 Condition* condition = Conditions::FromMessage(conditions); 1544 if (condition != NULL) { 1545 pass = condition->Test(*this); 1546 debug_printf("Test: %s -> %d\n", condition->ToString().String(), 1547 pass); 1548 delete condition; 1549 } else 1550 debug_printf("Could not parse condition!\n"); 1551 } 1552 1553 if (pass) { 1554 _AddRunTargets(runMessage, NULL); 1555 _AddRunTargets(runMessage, "then"); 1556 } else { 1557 _AddRunTargets(runMessage, "else"); 1558 } 1559 } 1560} 1561 1562 1563void 1564LaunchDaemon::_AddRunTargets(BMessage& message, const char* name) 1565{ 1566 BMessage targets; 1567 if (name != NULL && message.FindMessage(name, &targets) != B_OK) 1568 return; 1569 1570 const char* target; 1571 for (int32 index = 0; targets.FindString("target", index, &target) == B_OK; 1572 index++) { 1573 fRunTargets.Add(target); 1574 } 1575} 1576 1577 1578void 1579LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) 1580{ 1581 BString name = message.GetString("name"); 1582 if (name.IsEmpty()) { 1583 // Invalid job description 1584 return; 1585 } 1586 name.ToLower(); 1587 1588 Job* job = FindJob(name); 1589 if (job == NULL) { 1590 TRACE(" add job \"%s\"\n", name.String()); 1591 1592 job = new (std::nothrow) Job(name); 1593 if (job == NULL) 1594 return; 1595 1596 job->SetTeamListener(this); 1597 job->SetService(service); 1598 job->SetCreateDefaultPort(service); 1599 job->SetTarget(target); 1600 } else 1601 TRACE(" amend job \"%s\"\n", name.String()); 1602 1603 if (message.HasBool("disabled")) { 1604 job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled())); 1605 fLog.JobEnabled(job, job->IsEnabled()); 1606 } 1607 1608 if (message.HasBool("legacy")) 1609 job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); 1610 1611 _SetCondition(job, message); 1612 _SetEvent(job, message); 1613 _SetEnvironment(job, message); 1614 1615 BMessage portMessage; 1616 for (int32 index = 0; 1617 message.FindMessage("port", index, &portMessage) == B_OK; index++) { 1618 job->AddPort(portMessage); 1619 } 1620 1621 if (message.HasString("launch")) 1622 message.FindStrings("launch", &job->Arguments()); 1623 1624 const char* requirement; 1625 for (int32 index = 0; 1626 message.FindString("requires", index, &requirement) == B_OK; 1627 index++) { 1628 job->AddRequirement(requirement); 1629 } 1630 if (fInitTarget != NULL) 1631 job->AddRequirement(fInitTarget->Name()); 1632 1633 fJobs.insert(std::make_pair(job->Title(), job)); 1634} 1635 1636 1637/*! Initializes all jobs for the specified target (may be \c NULL). 1638 Jobs that cannot be initialized, and those that never will be due to 1639 conditions, will be removed from the list. 1640*/ 1641void 1642LaunchDaemon::_InitJobs(Target* target) 1643{ 1644 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) { 1645 Job* job = iterator->second; 1646 JobMap::iterator remove = iterator++; 1647 1648 if (job->Target() != target) 1649 continue; 1650 1651 status_t status = B_NO_INIT; 1652 if (job->IsEnabled()) { 1653 // Filter out jobs that have a constant and failing condition 1654 if (job->Condition() == NULL || !job->Condition()->IsConstant(*this) 1655 || job->Condition()->Test(*this)) { 1656 std::set<BString> dependencies; 1657 status = job->Init(*this, dependencies); 1658 if (status == B_OK && job->Event() != NULL) 1659 status = job->Event()->Register(*this); 1660 } 1661 } 1662 1663 if (status == B_OK) { 1664 fLog.JobInitialized(job); 1665 } else { 1666 if (status != B_NO_INIT) { 1667 // TODO: log error 1668 debug_printf("Init \"%s\" failed: %s\n", job->Name(), 1669 strerror(status)); 1670 } 1671 fLog.JobIgnored(job, status); 1672 1673 // Remove jobs that won't be used later on 1674 fJobs.erase(remove); 1675 delete job; 1676 } 1677 } 1678} 1679 1680 1681/*! Adds all jobs for the specified target (may be \c NULL) to the launch 1682 queue, except those that are triggered by events that haven't been 1683 triggered yet. 1684 1685 Unless \a forceNow is true, the target is only launched if its events, 1686 if any, have been triggered already, and its conditions are met. 1687*/ 1688void 1689LaunchDaemon::_LaunchJobs(Target* target, bool forceNow) 1690{ 1691 if (!forceNow && target != NULL && (!target->EventHasTriggered() 1692 || !target->CheckCondition(*this))) { 1693 return; 1694 } 1695 1696 if (target != NULL && !target->HasLaunched()) { 1697 target->SetLaunched(true); 1698 _InitJobs(target); 1699 } 1700 1701 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1702 iterator++) { 1703 Job* job = iterator->second; 1704 if (job->Target() == target) 1705 _LaunchJob(job); 1706 } 1707} 1708 1709 1710/*! Stops all running jobs of the specified target (may be \c NULL). 1711*/ 1712void 1713LaunchDaemon::_StopJobs(Target* target, bool force) 1714{ 1715 if (target != NULL && !target->HasLaunched()) 1716 return; 1717 1718 for (JobMap::reverse_iterator iterator = fJobs.rbegin(); 1719 iterator != fJobs.rend(); iterator++) { 1720 Job* job = iterator->second; 1721 if (job->Target() == target) 1722 _StopJob(job, force); 1723 } 1724} 1725 1726 1727/*! Checks whether or not the specified \a job can be launched. 1728 If \a testOnly is \c false, calling this method will trigger a demand 1729 to the \a job. 1730*/ 1731bool 1732LaunchDaemon::_CanLaunchJob(Job* job, uint32 options, bool testOnly) 1733{ 1734 if (job == NULL || !job->CanBeLaunched()) 1735 return false; 1736 1737 return (options & FORCE_NOW) != 0 1738 || (job->EventHasTriggered() && job->CheckCondition(*this) 1739 && ((options & TRIGGER_DEMAND) == 0 1740 || Events::TriggerDemand(job->Event(), testOnly))); 1741} 1742 1743 1744/*! Checks recursively if the requirements of the specified job can be launched, 1745 if they are not running already. 1746 Calling this method will not trigger a demand for the requirements. 1747*/ 1748bool 1749LaunchDaemon::_CanLaunchJobRequirements(Job* job, uint32 options) 1750{ 1751 int32 count = job->Requirements().CountStrings(); 1752 for (int32 index = 0; index < count; index++) { 1753 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1754 if (requirement != NULL 1755 && !requirement->IsRunning() && !requirement->IsLaunching() 1756 && (!_CanLaunchJob(requirement, options, true) 1757 || _CanLaunchJobRequirements(requirement, options))) { 1758 requirement->AddPending(job->Name()); 1759 return false; 1760 } 1761 } 1762 1763 return true; 1764} 1765 1766 1767/*! Adds the specified \a job to the launch queue 1768 queue, except those that are triggered by events. 1769 1770 Unless \c FORCE_NOW is set, the target is only launched if its events, 1771 if any, have been triggered already. 1772 1773 Calling this method will trigger a demand event if \c TRIGGER_DEMAND has 1774 been set. 1775*/ 1776bool 1777LaunchDaemon::_LaunchJob(Job* job, uint32 options) 1778{ 1779 if (job != NULL && (job->IsLaunching() || job->IsRunning())) 1780 return true; 1781 1782 if (!_CanLaunchJob(job, options)) 1783 return false; 1784 1785 // Test if we can launch all requirements 1786 if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND)) 1787 return false; 1788 1789 // Actually launch the requirements 1790 int32 count = job->Requirements().CountStrings(); 1791 for (int32 index = 0; index < count; index++) { 1792 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1793 if (requirement != NULL) { 1794 // TODO: For jobs that have their communication channels set up, 1795 // we would not need to trigger demand at this point 1796 if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) { 1797 // Failed to put a requirement into the launch queue 1798 return false; 1799 } 1800 } 1801 } 1802 1803 if (job->Target() != NULL) 1804 job->Target()->ResolveSourceFiles(); 1805 if (job->Event() != NULL) 1806 job->Event()->ResetTrigger(); 1807 1808 job->SetLaunching(true); 1809 1810 status_t status = fJobQueue.AddJob(job); 1811 if (status != B_OK) { 1812 debug_printf("Adding job %s to queue failed: %s\n", job->Name(), 1813 strerror(status)); 1814 return false; 1815 } 1816 1817 // Try to launch pending jobs as well 1818 count = job->Pending().CountStrings(); 1819 for (int32 index = 0; index < count; index++) { 1820 Job* pending = FindJob(job->Pending().StringAt(index)); 1821 if (pending != NULL && _LaunchJob(pending, 0)) { 1822 // Remove the job from the pending list once its in the launch 1823 // queue, so that is not being launched again next time. 1824 index--; 1825 count--; 1826 } 1827 } 1828 1829 return true; 1830} 1831 1832 1833void 1834LaunchDaemon::_StopJob(Job* job, bool force) 1835{ 1836 // TODO: find out which jobs require this job, and don't stop if any, 1837 // unless force, and then stop them all. 1838 job->SetEnabled(false); 1839 1840 if (!job->IsRunning()) 1841 return; 1842 1843 // Be nice first, and send a simple quit message 1844 BMessenger messenger; 1845 if (job->GetMessenger(messenger) == B_OK) { 1846 BMessage request(B_QUIT_REQUESTED); 1847 messenger.SendMessage(&request); 1848 1849 // TODO: wait a bit before going further 1850 return; 1851 } 1852 // TODO: allow custom shutdown 1853 1854 send_signal(-job->Team(), SIGINT); 1855 // TODO: this would be the next step, again, after a delay 1856 //send_signal(job->Team(), SIGKILL); 1857} 1858 1859 1860void 1861LaunchDaemon::_AddTarget(Target* target) 1862{ 1863 fTargets.insert(std::make_pair(target->Title(), target)); 1864} 1865 1866 1867void 1868LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message) 1869{ 1870 Condition* condition = job->Condition(); 1871 bool updated = false; 1872 1873 BMessage conditions; 1874 if (message.FindMessage("if", &conditions) == B_OK) { 1875 condition = Conditions::FromMessage(conditions); 1876 updated = true; 1877 } 1878 1879 if (message.GetBool("no_safemode")) { 1880 condition = Conditions::AddNotSafeMode(condition); 1881 updated = true; 1882 } 1883 1884 if (updated) 1885 job->SetCondition(condition); 1886} 1887 1888 1889void 1890LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message) 1891{ 1892 Event* event = job->Event(); 1893 bool updated = false; 1894 1895 BMessage events; 1896 if (message.FindMessage("on", &events) == B_OK) { 1897 event = Events::FromMessage(this, events); 1898 updated = true; 1899 } 1900 1901 if (message.GetBool("on_demand")) { 1902 event = Events::AddOnDemand(this, event); 1903 updated = true; 1904 } 1905 1906 if (updated) { 1907 TRACE(" event: %s\n", event->ToString().String()); 1908 job->SetEvent(event); 1909 } 1910} 1911 1912 1913void 1914LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message) 1915{ 1916 BMessage environmentMessage; 1917 if (message.FindMessage("env", &environmentMessage) == B_OK) 1918 job->SetEnvironment(environmentMessage); 1919} 1920 1921 1922ExternalEventSource* 1923LaunchDaemon::_FindExternalEventSource(const char* owner, const char* name) const 1924{ 1925 if (name == NULL) 1926 return NULL; 1927 1928 BString eventName = name; 1929 eventName.ToLower(); 1930 1931 EventMap::const_iterator found = fEvents.find(eventName); 1932 if (found != fEvents.end()) 1933 return found->second; 1934 1935 if (owner == NULL) 1936 return NULL; 1937 1938 eventName.Prepend("/"); 1939 eventName.Prepend(get_leaf(owner)); 1940 1941 found = fEvents.find(eventName); 1942 if (found != fEvents.end()) 1943 return found->second; 1944 1945 return NULL; 1946} 1947 1948 1949void 1950LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* eventSource, 1951 const BString& name) 1952{ 1953 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1954 iterator++) { 1955 Event* externalEvent = Events::ResolveExternalEvent(iterator->second->Event(), 1956 name, eventSource->Flags()); 1957 if (externalEvent != NULL) 1958 eventSource->AddDestination(externalEvent); 1959 } 1960} 1961 1962 1963void 1964LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info) 1965{ 1966 info.SetString("name", job->Name()); 1967 1968 if (job->Event() != NULL) 1969 info.SetString("event", job->Event()->ToString()); 1970 1971 if (job->Condition() != NULL) 1972 info.SetString("condition", job->Condition()->ToString()); 1973} 1974 1975 1976void 1977LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message) 1978{ 1979 if (fUserMode) 1980 return; 1981 1982 // Forward event to user launch_daemon(s) 1983 if (user == 0) { 1984 for (SessionMap::iterator iterator = fSessions.begin(); 1985 iterator != fSessions.end(); iterator++) { 1986 Session* session = iterator->second; 1987 session->Daemon().SendMessage(message); 1988 // ignore reply 1989 } 1990 } else { 1991 Session* session = FindSession(user); 1992 if (session != NULL) 1993 session->Daemon().SendMessage(message); 1994 } 1995} 1996 1997 1998status_t 1999LaunchDaemon::_StartSession(const char* login) 2000{ 2001 char path[B_PATH_NAME_LENGTH]; 2002 status_t status = get_app_path(path); 2003 if (status != B_OK) 2004 return status; 2005 2006 pid_t pid = -1; 2007 const char* argv[] = {path, login, NULL}; 2008 status = posix_spawn(&pid, path, NULL, NULL, (char* const*)argv, environ); 2009 if (status != B_OK) 2010 return status; 2011 2012 return B_OK; 2013} 2014 2015 2016void 2017LaunchDaemon::_RetrieveKernelOptions() 2018{ 2019 char buffer[32]; 2020 size_t size = sizeof(buffer); 2021 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer, 2022 &size); 2023 if (status == B_OK) { 2024 fSafeMode = !strncasecmp(buffer, "true", size) 2025 || !strncasecmp(buffer, "yes", size) 2026 || !strncasecmp(buffer, "on", size) 2027 || !strncasecmp(buffer, "enabled", size); 2028 } else 2029 fSafeMode = false; 2030} 2031 2032 2033void 2034LaunchDaemon::_SetupEnvironment() 2035{ 2036 // Determine safemode kernel option 2037 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true); 2038 2039 // Default locale settings 2040 setenv("LC_TYPE", "en_US.UTF-8", true); 2041} 2042 2043 2044/*! Basic system initialization that must happen before any jobs are launched. 2045*/ 2046void 2047LaunchDaemon::_InitSystem() 2048{ 2049#ifndef TEST_MODE 2050 _AddInitJob(new InitRealTimeClockJob()); 2051 _AddInitJob(new InitSharedMemoryDirectoryJob()); 2052 _AddInitJob(new InitTemporaryDirectoryJob()); 2053#endif 2054 2055 fJobQueue.AddJob(fInitTarget); 2056} 2057 2058 2059void 2060LaunchDaemon::_AddInitJob(BJob* job) 2061{ 2062 fInitTarget->AddDependency(job); 2063 fJobQueue.AddJob(job); 2064} 2065 2066 2067// #pragma mark - 2068 2069 2070#ifndef TEST_MODE 2071 2072 2073static void 2074open_stdio(int targetFD, int openMode) 2075{ 2076#ifdef DEBUG 2077 int fd = open("/dev/dprintf", openMode); 2078#else 2079 int fd = open("/dev/null", openMode); 2080#endif 2081 if (fd != targetFD) { 2082 dup2(fd, targetFD); 2083 close(fd); 2084 } 2085} 2086 2087 2088#endif // TEST_MODE 2089 2090 2091static int 2092user_main(const char* login) 2093{ 2094 struct passwd* passwd = getpwnam(login); 2095 if (passwd == NULL) 2096 return B_NAME_NOT_FOUND; 2097 if (strcmp(passwd->pw_name, login) != 0) 2098 return B_NAME_NOT_FOUND; 2099 2100 // Check if there is a user session running already 2101 uid_t user = passwd->pw_uid; 2102 gid_t group = passwd->pw_gid; 2103 2104 if (setsid() < 0) 2105 exit(EXIT_FAILURE); 2106 2107 if (initgroups(login, group) == -1) 2108 exit(EXIT_FAILURE); 2109 if (setgid(group) != 0) 2110 exit(EXIT_FAILURE); 2111 if (setuid(user) != 0) 2112 exit(EXIT_FAILURE); 2113 2114 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') { 2115 setenv("HOME", passwd->pw_dir, true); 2116 2117 if (chdir(passwd->pw_dir) != 0) { 2118 debug_printf("Could not switch to home dir %s: %s\n", 2119 passwd->pw_dir, strerror(errno)); 2120 } 2121 } 2122 2123 // TODO: take over system jobs, and reserve their names? (by asking parent) 2124 status_t status; 2125 LaunchDaemon* daemon = new LaunchDaemon(true, status); 2126 if (status == B_OK) 2127 daemon->Run(); 2128 2129 delete daemon; 2130 return 0; 2131} 2132 2133 2134int 2135main(int argc, char* argv[]) 2136{ 2137 if (argc == 2 && geteuid() == 0) 2138 return user_main(argv[1]); 2139 2140 if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) { 2141 fprintf(stderr, "The launch_daemon is already running!\n"); 2142 return EXIT_FAILURE; 2143 } 2144 2145#ifndef TEST_MODE 2146 // Make stdin/out/err available 2147 open_stdio(STDIN_FILENO, O_RDONLY); 2148 open_stdio(STDOUT_FILENO, O_WRONLY); 2149 dup2(STDOUT_FILENO, STDERR_FILENO); 2150#endif 2151 2152 status_t status; 2153 LaunchDaemon* daemon = new LaunchDaemon(false, status); 2154 if (status == B_OK) 2155 daemon->Run(); 2156 2157 delete daemon; 2158 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 2159} 2160