1/*
2 * Copyright 2015-2018, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "LaunchDaemon.h"
8
9#include <map>
10#include <set>
11
12#include <errno.h>
13#include <grp.h>
14#include <spawn.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18
19#include <Directory.h>
20#include <driver_settings.h>
21#include <Entry.h>
22#include <File.h>
23#include <ObjectList.h>
24#include <Path.h>
25#include <PathFinder.h>
26#include <Server.h>
27
28#include <AppMisc.h>
29#include <LaunchDaemonDefs.h>
30#include <LaunchRosterPrivate.h>
31#include <locks.h>
32#include <MessengerPrivate.h>
33#include <RosterPrivate.h>
34#include <syscalls.h>
35#include <system_info.h>
36
37#include "multiuser_utils.h"
38
39#include "Conditions.h"
40#include "Events.h"
41#include "InitRealTimeClockJob.h"
42#include "InitSharedMemoryDirectoryJob.h"
43#include "InitTemporaryDirectoryJob.h"
44#include "Job.h"
45#include "Log.h"
46#include "SettingsParser.h"
47#include "Target.h"
48#include "Utility.h"
49#include "Worker.h"
50
51
52#ifdef DEBUG
53#	define TRACE(x, ...) debug_printf(x, __VA_ARGS__)
54#	define TRACE_ONLY
55#else
56#	define TRACE(x, ...) ;
57#	define TRACE_ONLY __attribute__((unused))
58#endif
59
60
61using namespace ::BPrivate;
62using namespace BSupportKit;
63using BSupportKit::BPrivate::JobQueue;
64
65
66#ifndef TEST_MODE
67static const char* kLaunchDirectory = "launch";
68static const char* kUserLaunchDirectory = "user_launch";
69#endif
70
71
72enum launch_options {
73	FORCE_NOW		= 0x01,
74	TRIGGER_DEMAND	= 0x02
75};
76
77
78class Session {
79public:
80								Session(uid_t user, const BMessenger& target);
81
82			uid_t				User() const
83									{ return fUser; }
84			const BMessenger&	Daemon() const
85									{ return fDaemon; }
86
87private:
88			uid_t				fUser;
89			BMessenger			fDaemon;
90};
91
92
93/*!	This class is the connection between the external events that are part of
94	a job, and the external event source.
95
96	There is one object per registered event source, and it keeps all jobs that
97	reference the event as listeners. If the event source triggers the event,
98	the object will be used to trigger the jobs.
99*/
100class ExternalEventSource {
101public:
102								ExternalEventSource(BMessenger& source,
103									const char* ownerName,
104									const char* name, uint32 flags);
105								~ExternalEventSource();
106
107			const BMessenger&	Source() const
108									{ return fSource; }
109
110			const char*			Name() const;
111			const char*			OwnerName() const;
112			uint32				Flags() const
113									{ return fFlags; }
114
115			void				Trigger();
116			bool				StickyTriggered() const
117									{ return fStickyTriggered; }
118			void				ResetSticky();
119
120			status_t			AddDestination(Event* event);
121			void				RemoveDestination(Event* event);
122
123private:
124			BMessenger			fSource;
125			BString				fName, fOwnerName;
126			uint32				fFlags;
127			BObjectList<Event>	fDestinations;
128			bool				fStickyTriggered;
129};
130
131
132typedef std::map<BString, Job*> JobMap;
133typedef std::map<uid_t, Session*> SessionMap;
134typedef std::map<BString, Target*> TargetMap;
135typedef std::map<BString, ExternalEventSource*> EventMap;
136typedef std::map<team_id, Job*> TeamMap;
137
138
139class LaunchDaemon : public BServer, public Finder, public ConditionContext,
140	public EventRegistrator, public TeamListener {
141public:
142								LaunchDaemon(bool userMode, status_t& error);
143	virtual						~LaunchDaemon();
144
145	virtual	Job*				FindJob(const char* name) const;
146	virtual	Target*				FindTarget(const char* name) const;
147			Session*			FindSession(uid_t user) const;
148
149	// ConditionContext
150	virtual	bool				IsSafeMode() const;
151	virtual	bool				BootVolumeIsReadOnly() const;
152
153	// EventRegistrator
154	virtual	status_t			RegisterExternalEvent(Event* event,
155									const char* name,
156									const BStringList& arguments);
157	virtual	void				UnregisterExternalEvent(Event* event,
158									const char* name);
159
160	// TeamListener
161	virtual	void				TeamLaunched(Job* job, status_t status);
162
163	virtual	void				ReadyToRun();
164	virtual	void				MessageReceived(BMessage* message);
165
166private:
167			void				_HandleGetLaunchData(BMessage* message);
168			void				_HandleLaunchTarget(BMessage* message);
169			void				_HandleStopLaunchTarget(BMessage* message);
170			void				_HandleLaunchJob(BMessage* message);
171			void				_HandleEnableLaunchJob(BMessage* message);
172			void				_HandleStopLaunchJob(BMessage* message);
173			void				_HandleLaunchSession(BMessage* message);
174			void				_HandleRegisterSessionDaemon(BMessage* message);
175			void				_HandleRegisterLaunchEvent(BMessage* message);
176			void				_HandleUnregisterLaunchEvent(BMessage* message);
177			void				_HandleNotifyLaunchEvent(BMessage* message);
178			void				_HandleResetStickyLaunchEvent(
179									BMessage* message);
180			void				_HandleGetLaunchTargets(BMessage* message);
181			void				_HandleGetLaunchTargetInfo(BMessage* message);
182			void				_HandleGetLaunchJobs(BMessage* message);
183			void				_HandleGetLaunchJobInfo(BMessage* message);
184			void				_HandleGetLaunchLog(BMessage* message);
185			uid_t				_GetUserID(BMessage* message);
186
187			void				_ReadPaths(const BStringList& paths);
188			void				_ReadEntry(const char* context, BEntry& entry);
189			void				_ReadDirectory(const char* context,
190									BEntry& directory);
191			status_t			_ReadFile(const char* context, BEntry& entry);
192
193			void				_AddJobs(Target* target, BMessage& message);
194			void				_AddTargets(BMessage& message);
195			void				_AddRunTargets(BMessage& message);
196			void				_AddRunTargets(BMessage& message,
197									const char* name);
198			void				_AddJob(Target* target, bool service,
199									BMessage& message);
200			void				_InitJobs(Target* target);
201			void				_LaunchJobs(Target* target,
202									bool forceNow = false);
203			void				_StopJobs(Target* target, bool force);
204			bool				_CanLaunchJob(Job* job, uint32 options,
205									bool testOnly = false);
206			bool				_CanLaunchJobRequirements(Job* job,
207									uint32 options);
208			bool				_LaunchJob(Job* job, uint32 options = 0);
209			void				_StopJob(Job* job, bool force);
210			void				_AddTarget(Target* target);
211			void				_SetCondition(BaseJob* job,
212									const BMessage& message);
213			void				_SetEvent(BaseJob* job,
214									const BMessage& message);
215			void				_SetEnvironment(BaseJob* job,
216									const BMessage& message);
217
218			ExternalEventSource*
219								_FindExternalEventSource(const char* owner,
220									const char* name) const;
221			void				_ResolveExternalEvents(
222									ExternalEventSource* event,
223									const BString& name);
224			void				_GetBaseJobInfo(BaseJob* job, BMessage& info);
225			void				_ForwardEventMessage(uid_t user,
226									BMessage* message);
227
228			status_t			_StartSession(const char* login);
229
230			void				_RetrieveKernelOptions();
231			void				_SetupEnvironment();
232			void				_InitSystem();
233			void				_AddInitJob(BJob* job);
234
235private:
236			Log					fLog;
237			JobMap				fJobs;
238			TargetMap			fTargets;
239			BStringList			fRunTargets;
240			EventMap			fEvents;
241			JobQueue			fJobQueue;
242			SessionMap			fSessions;
243			MainWorker*			fMainWorker;
244			Target*				fInitTarget;
245			TeamMap				fTeams;
246			mutex				fTeamsLock;
247			bool				fSafeMode;
248			bool				fReadOnlyBootVolume;
249			bool				fUserMode;
250};
251
252
253static const char*
254get_leaf(const char* signature)
255{
256	if (signature == NULL)
257		return NULL;
258
259	const char* separator = strrchr(signature, '/');
260	if (separator != NULL)
261		return separator + 1;
262
263	return signature;
264}
265
266
267// #pragma mark -
268
269
270Session::Session(uid_t user, const BMessenger& daemon)
271	:
272	fUser(user),
273	fDaemon(daemon)
274{
275}
276
277
278// #pragma mark -
279
280
281ExternalEventSource::ExternalEventSource(BMessenger& source,
282	const char* ownerName, const char* name, uint32 flags)
283	:
284	fSource(source),
285	fName(name),
286	fOwnerName(ownerName),
287	fFlags(flags),
288	fDestinations(5),
289	fStickyTriggered(false)
290{
291}
292
293
294ExternalEventSource::~ExternalEventSource()
295{
296}
297
298
299const char*
300ExternalEventSource::Name() const
301{
302	return fName.String();
303}
304
305
306const char*
307ExternalEventSource::OwnerName() const
308{
309	return fOwnerName.String();
310}
311
312
313void
314ExternalEventSource::Trigger()
315{
316	for (int32 index = 0; index < fDestinations.CountItems(); index++)
317		Events::TriggerExternalEvent(fDestinations.ItemAt(index));
318
319	if ((fFlags & B_STICKY_EVENT) != 0)
320		fStickyTriggered = true;
321}
322
323
324void
325ExternalEventSource::ResetSticky()
326{
327	if ((fFlags & B_STICKY_EVENT) != 0)
328		fStickyTriggered = false;
329
330	for (int32 index = 0; index < fDestinations.CountItems(); index++)
331		Events::ResetStickyExternalEvent(fDestinations.ItemAt(index));
332}
333
334
335status_t
336ExternalEventSource::AddDestination(Event* event)
337{
338	if (fStickyTriggered)
339		Events::TriggerExternalEvent(event);
340
341	if (fDestinations.AddItem(event))
342		return B_OK;
343
344	return B_NO_MEMORY;
345}
346
347
348void
349ExternalEventSource::RemoveDestination(Event* event)
350{
351	fDestinations.RemoveItem(event);
352}
353
354
355// #pragma mark -
356
357
358LaunchDaemon::LaunchDaemon(bool userMode, status_t& error)
359	:
360	BServer(kLaunchDaemonSignature, NULL,
361		create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
362			userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error),
363	fInitTarget(userMode ? NULL : new Target("init")),
364#ifdef TEST_MODE
365	fUserMode(true)
366#else
367	fUserMode(userMode)
368#endif
369{
370	mutex_init(&fTeamsLock, "teams lock");
371
372	fMainWorker = new MainWorker(fJobQueue);
373	fMainWorker->Init();
374
375	if (fInitTarget != NULL)
376		_AddTarget(fInitTarget);
377
378	// We may not be able to talk to the registrar
379	if (!fUserMode)
380		BRoster::Private().SetWithoutRegistrar(true);
381}
382
383
384LaunchDaemon::~LaunchDaemon()
385{
386}
387
388
389Job*
390LaunchDaemon::FindJob(const char* name) const
391{
392	if (name == NULL)
393		return NULL;
394
395	JobMap::const_iterator found = fJobs.find(BString(name).ToLower());
396	if (found != fJobs.end())
397		return found->second;
398
399	return NULL;
400}
401
402
403Target*
404LaunchDaemon::FindTarget(const char* name) const
405{
406	if (name == NULL)
407		return NULL;
408
409	TargetMap::const_iterator found = fTargets.find(BString(name).ToLower());
410	if (found != fTargets.end())
411		return found->second;
412
413	return NULL;
414}
415
416
417Session*
418LaunchDaemon::FindSession(uid_t user) const
419{
420	SessionMap::const_iterator found = fSessions.find(user);
421	if (found != fSessions.end())
422		return found->second;
423
424	return NULL;
425}
426
427
428bool
429LaunchDaemon::IsSafeMode() const
430{
431	return fSafeMode;
432}
433
434
435bool
436LaunchDaemon::BootVolumeIsReadOnly() const
437{
438	return fReadOnlyBootVolume;
439}
440
441
442status_t
443LaunchDaemon::RegisterExternalEvent(Event* event, const char* name,
444	const BStringList& arguments)
445{
446	status_t status TRACE_ONLY = B_NAME_NOT_FOUND;
447	for (EventMap::iterator iterator = fEvents.begin();
448			iterator != fEvents.end(); iterator++) {
449		ExternalEventSource* eventSource = iterator->second;
450		Event* externalEvent = Events::ResolveExternalEvent(event,
451			eventSource->Name(), eventSource->Flags());
452		if (externalEvent != NULL) {
453			status = eventSource->AddDestination(event);
454			break;
455		}
456	}
457
458	TRACE("Register external event '%s': %" B_PRId32 "\n", name, status);
459
460	// Even if we failed to find a matching source, we do not want to return an error,
461	// as that will be propagated up the chain and prevent this job from being instantiated.
462	// Jobs will be re-scanned later for unregistered external events.
463	return B_OK;
464}
465
466
467void
468LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name)
469{
470	for (EventMap::iterator iterator = fEvents.begin();
471			iterator != fEvents.end(); iterator++) {
472		ExternalEventSource* eventSource = iterator->second;
473		Event* externalEvent = Events::ResolveExternalEvent(event,
474			eventSource->Name(), eventSource->Flags());
475		if (externalEvent != NULL) {
476			eventSource->RemoveDestination(event);
477			break;
478		}
479	}
480}
481
482
483void
484LaunchDaemon::TeamLaunched(Job* job, status_t status)
485{
486	fLog.JobLaunched(job, status);
487
488	MutexLocker locker(fTeamsLock);
489	fTeams.insert(std::make_pair(job->Team(), job));
490}
491
492
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