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", &parameter);
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