xref: /haiku/src/servers/launch/LaunchDaemon.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 
18 #include <Directory.h>
19 #include <driver_settings.h>
20 #include <Entry.h>
21 #include <File.h>
22 #include <ObjectList.h>
23 #include <Path.h>
24 #include <PathFinder.h>
25 #include <Server.h>
26 
27 #include <AppMisc.h>
28 #include <LaunchDaemonDefs.h>
29 #include <LaunchRosterPrivate.h>
30 #include <locks.h>
31 #include <MessengerPrivate.h>
32 #include <RosterPrivate.h>
33 #include <syscalls.h>
34 #include <system_info.h>
35 
36 #include "multiuser_utils.h"
37 
38 #include "Conditions.h"
39 #include "Events.h"
40 #include "InitRealTimeClockJob.h"
41 #include "InitSharedMemoryDirectoryJob.h"
42 #include "InitTemporaryDirectoryJob.h"
43 #include "Job.h"
44 #include "Log.h"
45 #include "SettingsParser.h"
46 #include "Target.h"
47 #include "Utility.h"
48 #include "Worker.h"
49 
50 
51 #ifdef DEBUG
52 #	define TRACE(x, ...) debug_printf(x, __VA_ARGS__)
53 #else
54 #	define TRACE(x, ...) ;
55 #endif
56 
57 
58 using namespace ::BPrivate;
59 using namespace BSupportKit;
60 using BSupportKit::BPrivate::JobQueue;
61 
62 
63 #ifndef TEST_MODE
64 static const char* kLaunchDirectory = "launch";
65 static const char* kUserLaunchDirectory = "user_launch";
66 #endif
67 
68 
69 enum launch_options {
70 	FORCE_NOW		= 0x01,
71 	TRIGGER_DEMAND	= 0x02
72 };
73 
74 
75 class Session {
76 public:
77 								Session(uid_t user, const BMessenger& target);
78 
79 			uid_t				User() const
80 									{ return fUser; }
81 			const BMessenger&	Daemon() const
82 									{ return fDaemon; }
83 
84 private:
85 			uid_t				fUser;
86 			BMessenger			fDaemon;
87 };
88 
89 
90 /*!	This class is the connection between the external events that are part of
91 	a job, and the external event source.
92 
93 	There is one object per registered event source, and it keeps all jobs that
94 	reference the event as listeners. If the event source triggers the event,
95 	the object will be used to trigger the jobs.
96 */
97 class ExternalEventSource {
98 public:
99 								ExternalEventSource(BMessenger& source,
100 									const char* ownerName,
101 									const char* name, uint32 flags);
102 								~ExternalEventSource();
103 
104 			const char*			Name() const;
105 			uint32				Flags() const
106 									{ return fFlags; }
107 
108 			void				Trigger();
109 			void				ResetSticky();
110 
111 			status_t			AddDestination(Event* event);
112 			void				RemoveDestination(Event* event);
113 
114 private:
115 			BString				fName;
116 			uint32				fFlags;
117 			BObjectList<Event>	fDestinations;
118 			bool				fStickyTriggered;
119 };
120 
121 
122 typedef std::map<BString, Job*> JobMap;
123 typedef std::map<uid_t, Session*> SessionMap;
124 typedef std::map<BString, Target*> TargetMap;
125 typedef std::map<BString, ExternalEventSource*> EventMap;
126 typedef std::map<team_id, Job*> TeamMap;
127 
128 
129 class LaunchDaemon : public BServer, public Finder, public ConditionContext,
130 	public EventRegistrator, public TeamListener {
131 public:
132 								LaunchDaemon(bool userMode,
133 									const EventMap& events, status_t& error);
134 	virtual						~LaunchDaemon();
135 
136 	virtual	Job*				FindJob(const char* name) const;
137 	virtual	Target*				FindTarget(const char* name) const;
138 			Session*			FindSession(uid_t user) const;
139 
140 	// ConditionContext
141 	virtual	bool				IsSafeMode() const;
142 	virtual	bool				BootVolumeIsReadOnly() const;
143 
144 	// EventRegistrator
145 	virtual	status_t			RegisterExternalEvent(Event* event,
146 									const char* name,
147 									const BStringList& arguments);
148 	virtual	void				UnregisterExternalEvent(Event* event,
149 									const char* name);
150 
151 	// TeamListener
152 	virtual	void				TeamLaunched(Job* job, status_t status);
153 
154 	virtual	void				ReadyToRun();
155 	virtual	void				MessageReceived(BMessage* message);
156 
157 private:
158 			void				_HandleGetLaunchData(BMessage* message);
159 			void				_HandleLaunchTarget(BMessage* message);
160 			void				_HandleStopLaunchTarget(BMessage* message);
161 			void				_HandleLaunchJob(BMessage* message);
162 			void				_HandleEnableLaunchJob(BMessage* message);
163 			void				_HandleStopLaunchJob(BMessage* message);
164 			void				_HandleLaunchSession(BMessage* message);
165 			void				_HandleRegisterSessionDaemon(BMessage* message);
166 			void				_HandleRegisterLaunchEvent(BMessage* message);
167 			void				_HandleUnregisterLaunchEvent(BMessage* message);
168 			void				_HandleNotifyLaunchEvent(BMessage* message);
169 			void				_HandleResetStickyLaunchEvent(
170 									BMessage* message);
171 			void				_HandleGetLaunchTargets(BMessage* message);
172 			void				_HandleGetLaunchTargetInfo(BMessage* message);
173 			void				_HandleGetLaunchJobs(BMessage* message);
174 			void				_HandleGetLaunchJobInfo(BMessage* message);
175 			void				_HandleGetLaunchLog(BMessage* message);
176 			uid_t				_GetUserID(BMessage* message);
177 
178 			void				_ReadPaths(const BStringList& paths);
179 			void				_ReadEntry(const char* context, BEntry& entry);
180 			void				_ReadDirectory(const char* context,
181 									BEntry& directory);
182 			status_t			_ReadFile(const char* context, BEntry& entry);
183 
184 			void				_AddJobs(Target* target, BMessage& message);
185 			void				_AddTargets(BMessage& message);
186 			void				_AddRunTargets(BMessage& message);
187 			void				_AddRunTargets(BMessage& message,
188 									const char* name);
189 			void				_AddJob(Target* target, bool service,
190 									BMessage& message);
191 			void				_InitJobs(Target* target);
192 			void				_LaunchJobs(Target* target,
193 									bool forceNow = false);
194 			void				_StopJobs(Target* target, bool force);
195 			bool				_CanLaunchJob(Job* job, uint32 options,
196 									bool testOnly = false);
197 			bool				_CanLaunchJobRequirements(Job* job,
198 									uint32 options);
199 			bool				_LaunchJob(Job* job, uint32 options = 0);
200 			void				_StopJob(Job* job, bool force);
201 			void				_AddTarget(Target* target);
202 			void				_SetCondition(BaseJob* job,
203 									const BMessage& message);
204 			void				_SetEvent(BaseJob* job,
205 									const BMessage& message);
206 			void				_SetEnvironment(BaseJob* job,
207 									const BMessage& message);
208 
209 			ExternalEventSource*
210 								_FindExternalEventSource(const char* owner,
211 									const char* name) const;
212 			void				_ResolveExternalEvents(
213 									ExternalEventSource* event,
214 									const BString& name);
215 			void				_GetBaseJobInfo(BaseJob* job, BMessage& info);
216 			void				_ForwardEventMessage(uid_t user,
217 									BMessage* message);
218 
219 			status_t			_StartSession(const char* login);
220 
221 			void				_RetrieveKernelOptions();
222 			void				_SetupEnvironment();
223 			void				_InitSystem();
224 			void				_AddInitJob(BJob* job);
225 
226 private:
227 			Log					fLog;
228 			JobMap				fJobs;
229 			TargetMap			fTargets;
230 			BStringList			fRunTargets;
231 			EventMap			fEvents;
232 			JobQueue			fJobQueue;
233 			SessionMap			fSessions;
234 			MainWorker*			fMainWorker;
235 			Target*				fInitTarget;
236 			TeamMap				fTeams;
237 			mutex				fTeamsLock;
238 			bool				fSafeMode;
239 			bool				fReadOnlyBootVolume;
240 			bool				fUserMode;
241 };
242 
243 
244 static const char*
245 get_leaf(const char* signature)
246 {
247 	if (signature == NULL)
248 		return NULL;
249 
250 	const char* separator = strrchr(signature, '/');
251 	if (separator != NULL)
252 		return separator + 1;
253 
254 	return signature;
255 }
256 
257 
258 // #pragma mark -
259 
260 
261 Session::Session(uid_t user, const BMessenger& daemon)
262 	:
263 	fUser(user),
264 	fDaemon(daemon)
265 {
266 }
267 
268 
269 // #pragma mark -
270 
271 
272 ExternalEventSource::ExternalEventSource(BMessenger& source,
273 	const char* ownerName, const char* name, uint32 flags)
274 	:
275 	fName(name),
276 	fFlags(flags),
277 	fDestinations(5),
278 	fStickyTriggered(false)
279 {
280 }
281 
282 
283 ExternalEventSource::~ExternalEventSource()
284 {
285 }
286 
287 
288 const char*
289 ExternalEventSource::Name() const
290 {
291 	return fName.String();
292 }
293 
294 
295 void
296 ExternalEventSource::Trigger()
297 {
298 	for (int32 index = 0; index < fDestinations.CountItems(); index++)
299 		Events::TriggerExternalEvent(fDestinations.ItemAt(index));
300 
301 	if ((fFlags & B_STICKY_EVENT) != 0)
302 		fStickyTriggered = true;
303 }
304 
305 
306 void
307 ExternalEventSource::ResetSticky()
308 {
309 	if ((fFlags & B_STICKY_EVENT) != 0)
310 		fStickyTriggered = false;
311 
312 	for (int32 index = 0; index < fDestinations.CountItems(); index++)
313 		Events::ResetStickyExternalEvent(fDestinations.ItemAt(index));
314 }
315 
316 
317 status_t
318 ExternalEventSource::AddDestination(Event* event)
319 {
320 	if (fStickyTriggered)
321 		Events::TriggerExternalEvent(event);
322 
323 	if (fDestinations.AddItem(event))
324 		return B_OK;
325 
326 	return B_NO_MEMORY;
327 }
328 
329 
330 void
331 ExternalEventSource::RemoveDestination(Event* event)
332 {
333 	fDestinations.RemoveItem(event);
334 }
335 
336 
337 // #pragma mark -
338 
339 
340 LaunchDaemon::LaunchDaemon(bool userMode, const EventMap& events,
341 	status_t& error)
342 	:
343 	BServer(kLaunchDaemonSignature, NULL,
344 		create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
345 			userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error),
346 	fEvents(events),
347 	fInitTarget(userMode ? NULL : new Target("init")),
348 #ifdef TEST_MODE
349 	fUserMode(true)
350 #else
351 	fUserMode(userMode)
352 #endif
353 {
354 	mutex_init(&fTeamsLock, "teams lock");
355 
356 	fMainWorker = new MainWorker(fJobQueue);
357 	fMainWorker->Init();
358 
359 	if (fInitTarget != NULL)
360 		_AddTarget(fInitTarget);
361 
362 	// We may not be able to talk to the registrar
363 	if (!fUserMode)
364 		BRoster::Private().SetWithoutRegistrar(true);
365 }
366 
367 
368 LaunchDaemon::~LaunchDaemon()
369 {
370 }
371 
372 
373 Job*
374 LaunchDaemon::FindJob(const char* name) const
375 {
376 	if (name == NULL)
377 		return NULL;
378 
379 	JobMap::const_iterator found = fJobs.find(BString(name).ToLower());
380 	if (found != fJobs.end())
381 		return found->second;
382 
383 	return NULL;
384 }
385 
386 
387 Target*
388 LaunchDaemon::FindTarget(const char* name) const
389 {
390 	if (name == NULL)
391 		return NULL;
392 
393 	TargetMap::const_iterator found = fTargets.find(BString(name).ToLower());
394 	if (found != fTargets.end())
395 		return found->second;
396 
397 	return NULL;
398 }
399 
400 
401 Session*
402 LaunchDaemon::FindSession(uid_t user) const
403 {
404 	SessionMap::const_iterator found = fSessions.find(user);
405 	if (found != fSessions.end())
406 		return found->second;
407 
408 	return NULL;
409 }
410 
411 
412 bool
413 LaunchDaemon::IsSafeMode() const
414 {
415 	return fSafeMode;
416 }
417 
418 
419 bool
420 LaunchDaemon::BootVolumeIsReadOnly() const
421 {
422 	return fReadOnlyBootVolume;
423 }
424 
425 
426 status_t
427 LaunchDaemon::RegisterExternalEvent(Event* event, const char* name,
428 	const BStringList& arguments)
429 {
430 	status_t status = B_NAME_NOT_FOUND;
431 	for (EventMap::iterator iterator = fEvents.begin();
432 			iterator != fEvents.end(); iterator++) {
433 		ExternalEventSource* eventSource = iterator->second;
434 		Event* externalEvent = Events::ResolveExternalEvent(event,
435 			eventSource->Name(), eventSource->Flags());
436 		if (externalEvent != NULL) {
437 			status = eventSource->AddDestination(event);
438 			break;
439 		}
440 	}
441 
442 	TRACE("Register external event '%s': %" B_PRId32 "\n", name, status);
443 
444 	// Even if we failed to find a matching source, we do not want to return an error,
445 	// as that will be propagated up the chain and prevent this job from being instantiated.
446 	// Jobs will be re-scanned later for unregistered external events.
447 	return B_OK;
448 }
449 
450 
451 void
452 LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name)
453 {
454 	for (EventMap::iterator iterator = fEvents.begin();
455 			iterator != fEvents.end(); iterator++) {
456 		ExternalEventSource* eventSource = iterator->second;
457 		Event* externalEvent = Events::ResolveExternalEvent(event,
458 			eventSource->Name(), eventSource->Flags());
459 		if (externalEvent != NULL) {
460 			eventSource->RemoveDestination(event);
461 			break;
462 		}
463 	}
464 }
465 
466 
467 void
468 LaunchDaemon::TeamLaunched(Job* job, status_t status)
469 {
470 	fLog.JobLaunched(job, status);
471 
472 	MutexLocker locker(fTeamsLock);
473 	fTeams.insert(std::make_pair(job->Team(), job));
474 }
475 
476 
477 void
478 LaunchDaemon::ReadyToRun()
479 {
480 	_RetrieveKernelOptions();
481 	_SetupEnvironment();
482 
483 	fReadOnlyBootVolume = Utility::IsReadOnlyVolume("/boot");
484 	if (fReadOnlyBootVolume)
485 		Utility::BlockMedia("/boot", true);
486 
487 	if (fUserMode) {
488 #ifndef TEST_MODE
489 		BLaunchRoster roster;
490 		BLaunchRoster::Private(roster).RegisterSessionDaemon(this);
491 #endif	// TEST_MODE
492 	} else
493 		_InitSystem();
494 
495 	BStringList paths;
496 #ifdef TEST_MODE
497 	paths.Add("/boot/home/test_launch");
498 #else
499 	if (fUserMode) {
500 		// System-wide user specific jobs
501 		BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kUserLaunchDirectory,
502 			B_FIND_PATHS_SYSTEM_ONLY, paths);
503 		_ReadPaths(paths);
504 	}
505 
506 	BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory,
507 		fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
508 	_ReadPaths(paths);
509 
510 	if (fUserMode) {
511 		BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY,
512 			kUserLaunchDirectory, B_FIND_PATHS_SYSTEM_ONLY, paths);
513 		_ReadPaths(paths);
514 	}
515 
516 	BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory,
517 		fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
518 #endif	// TEST_MODE
519 	_ReadPaths(paths);
520 
521 	BMessenger target(this);
522 	BMessenger::Private messengerPrivate(target);
523 	port_id port = messengerPrivate.Port();
524 	int32 token = messengerPrivate.Token();
525 	__start_watching_system(-1, B_WATCH_SYSTEM_TEAM_DELETION, port, token);
526 
527 	_InitJobs(NULL);
528 	_LaunchJobs(NULL);
529 
530 	// Launch run targets (ignores events)
531 	for (int32 index = 0; index < fRunTargets.CountStrings(); index++) {
532 		Target* target = FindTarget(fRunTargets.StringAt(index));
533 		if (target != NULL)
534 			_LaunchJobs(target);
535 	}
536 
537 	if (fUserMode)
538 		be_roster->StartWatching(this, B_REQUEST_LAUNCHED);
539 }
540 
541 
542 void
543 LaunchDaemon::MessageReceived(BMessage* message)
544 {
545 	switch (message->what) {
546 		case B_SYSTEM_OBJECT_UPDATE:
547 		{
548 			int32 opcode = message->GetInt32("opcode", 0);
549 			team_id team = (team_id)message->GetInt32("team", -1);
550 			if (opcode != B_TEAM_DELETED || team < 0)
551 				break;
552 
553 			MutexLocker locker(fTeamsLock);
554 
555 			TeamMap::iterator found = fTeams.find(team);
556 			if (found != fTeams.end()) {
557 				Job* job = found->second;
558 				TRACE("Job %s ended!\n", job->Name());
559 
560 				// Get the exit status, and pass it on to the log
561 				status_t exitStatus = B_OK;
562 				wait_for_thread(team, &exitStatus);
563 				fLog.JobTerminated(job, exitStatus);
564 				job->TeamDeleted();
565 
566 				if (job->IsService()) {
567 					bool inProgress = false;
568 					BRoster roster;
569 					BRoster::Private rosterPrivate(roster);
570 					status_t status = rosterPrivate.IsShutDownInProgress(
571 						&inProgress);
572 					if (status != B_OK || !inProgress) {
573 						// TODO: take restart throttle into account
574 						_LaunchJob(job);
575 					}
576 				}
577 			}
578 			break;
579 		}
580 		case B_SOME_APP_LAUNCHED:
581 		{
582 			team_id team = (team_id)message->GetInt32("be:team", -1);
583 			Job* job = NULL;
584 
585 			MutexLocker locker(fTeamsLock);
586 
587 			TeamMap::iterator found = fTeams.find(team);
588 			if (found != fTeams.end()) {
589 				job = found->second;
590 				locker.Unlock();
591 			} else {
592 				locker.Unlock();
593 
594 				// Find job by name instead
595 				const char* signature = message->GetString("be:signature");
596 				job = FindJob(get_leaf(signature));
597 				if (job != NULL) {
598 					TRACE("Updated default port of untracked team %d, %s\n",
599 						(int)team, signature);
600 				}
601 			}
602 
603 			if (job != NULL) {
604 				// Update port info
605 				app_info info;
606 				status_t status = be_roster->GetRunningAppInfo(team, &info);
607 				if (status == B_OK && info.port != job->DefaultPort()) {
608 					TRACE("Update default port for %s to %d\n", job->Name(),
609 						(int)info.port);
610 					job->SetDefaultPort(info.port);
611 				}
612 			}
613 			break;
614 		}
615 
616 		case B_GET_LAUNCH_DATA:
617 			_HandleGetLaunchData(message);
618 			break;
619 
620 		case B_LAUNCH_TARGET:
621 			_HandleLaunchTarget(message);
622 			break;
623 		case B_STOP_LAUNCH_TARGET:
624 			_HandleStopLaunchTarget(message);
625 			break;
626 		case B_LAUNCH_JOB:
627 			_HandleLaunchJob(message);
628 			break;
629 		case B_ENABLE_LAUNCH_JOB:
630 			_HandleEnableLaunchJob(message);
631 			break;
632 		case B_STOP_LAUNCH_JOB:
633 			_HandleStopLaunchJob(message);
634 			break;
635 
636 		case B_LAUNCH_SESSION:
637 			_HandleLaunchSession(message);
638 			break;
639 		case B_REGISTER_SESSION_DAEMON:
640 			_HandleRegisterSessionDaemon(message);
641 			break;
642 
643 		case B_REGISTER_LAUNCH_EVENT:
644 			_HandleRegisterLaunchEvent(message);
645 			break;
646 		case B_UNREGISTER_LAUNCH_EVENT:
647 			_HandleUnregisterLaunchEvent(message);
648 			break;
649 		case B_NOTIFY_LAUNCH_EVENT:
650 			_HandleNotifyLaunchEvent(message);
651 			break;
652 		case B_RESET_STICKY_LAUNCH_EVENT:
653 			_HandleResetStickyLaunchEvent(message);
654 			break;
655 
656 		case B_GET_LAUNCH_TARGETS:
657 			_HandleGetLaunchTargets(message);
658 			break;
659 		case B_GET_LAUNCH_TARGET_INFO:
660 			_HandleGetLaunchTargetInfo(message);
661 			break;
662 		case B_GET_LAUNCH_JOBS:
663 			_HandleGetLaunchJobs(message);
664 			break;
665 		case B_GET_LAUNCH_JOB_INFO:
666 			_HandleGetLaunchJobInfo(message);
667 			break;
668 
669 		case B_GET_LAUNCH_LOG:
670 			_HandleGetLaunchLog(message);
671 			break;
672 
673 		case kMsgEventTriggered:
674 		{
675 			// An internal event has been triggered.
676 			// Check if its job(s) can be launched now.
677 			const char* name = message->GetString("owner");
678 			if (name == NULL)
679 				break;
680 
681 			Event* event = (Event*)message->GetPointer("event");
682 
683 			Job* job = FindJob(name);
684 			if (job != NULL) {
685 				fLog.EventTriggered(job, event);
686 				_LaunchJob(job);
687 				break;
688 			}
689 
690 			Target* target = FindTarget(name);
691 			if (target != NULL) {
692 				fLog.EventTriggered(target, event);
693 				_LaunchJobs(target);
694 				break;
695 			}
696 			break;
697 		}
698 
699 		default:
700 			BServer::MessageReceived(message);
701 			break;
702 	}
703 }
704 
705 
706 void
707 LaunchDaemon::_HandleGetLaunchData(BMessage* message)
708 {
709 	uid_t user = _GetUserID(message);
710 	if (user < 0)
711 		return;
712 
713 	BMessage reply((uint32)B_OK);
714 	bool launchJob = true;
715 
716 	Job* job = FindJob(get_leaf(message->GetString("name")));
717 	if (job == NULL) {
718 		Session* session = FindSession(user);
719 		if (session != NULL) {
720 			// Forward request to user launch_daemon
721 			if (session->Daemon().SendMessage(message) == B_OK)
722 				return;
723 		}
724 		reply.what = B_NAME_NOT_FOUND;
725 	} else if (job->IsService() && !job->IsLaunched()) {
726 		if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) {
727 			// The job exists, but cannot be started yet, as its
728 			// conditions are not met; don't make it available yet
729 			// TODO: we may not want to initialize jobs with conditions
730 			// that aren't met yet
731 			reply.what = B_NO_INIT;
732 		} else if (job->Event() != NULL) {
733 			if (!Events::TriggerDemand(job->Event())) {
734 				// The job is not triggered by demand; we cannot start it now
735 				reply.what = B_NO_INIT;
736 			} else {
737 				// The job has already been triggered, don't launch it again
738 				launchJob = false;
739 			}
740 		}
741 	} else
742 		launchJob = false;
743 
744 	bool ownsMessage = false;
745 	if (reply.what == B_OK) {
746 		// Launch the job if it hasn't been launched already
747 		if (launchJob)
748 			_LaunchJob(job, TRIGGER_DEMAND);
749 
750 		DetachCurrentMessage();
751 		status_t result = job->HandleGetLaunchData(message);
752 		if (result == B_OK) {
753 			// Replying is delegated to the job.
754 			return;
755 		}
756 
757 		ownsMessage = true;
758 		reply.what = result;
759 	}
760 
761 	message->SendReply(&reply);
762 	if (ownsMessage)
763 		delete message;
764 }
765 
766 
767 void
768 LaunchDaemon::_HandleLaunchTarget(BMessage* message)
769 {
770 	uid_t user = _GetUserID(message);
771 	if (user < 0)
772 		return;
773 
774 	const char* name = message->GetString("target");
775 	const char* baseName = message->GetString("base target");
776 
777 	Target* target = FindTarget(name);
778 	if (target == NULL && baseName != NULL) {
779 		Target* baseTarget = FindTarget(baseName);
780 		if (baseTarget != NULL) {
781 			target = new Target(name);
782 
783 			// Copy all jobs with the base target into the new target
784 			for (JobMap::iterator iterator = fJobs.begin();
785 					iterator != fJobs.end();) {
786 				Job* job = iterator->second;
787 				iterator++;
788 
789 				if (job->Target() == baseTarget) {
790 					Job* copy = new Job(*job);
791 					copy->SetTarget(target);
792 
793 					fJobs.insert(std::make_pair(copy->Name(), copy));
794 				}
795 			}
796 		}
797 	}
798 	if (target == NULL) {
799 		Session* session = FindSession(user);
800 		if (session != NULL) {
801 			// Forward request to user launch_daemon
802 			if (session->Daemon().SendMessage(message) == B_OK)
803 				return;
804 		}
805 
806 		BMessage reply(B_NAME_NOT_FOUND);
807 		message->SendReply(&reply);
808 		return;
809 	}
810 
811 	BMessage data;
812 	if (message->FindMessage("data", &data) == B_OK)
813 		target->AddData(data.GetString("name"), data);
814 
815 	_LaunchJobs(target);
816 
817 	BMessage reply((uint32)B_OK);
818 	message->SendReply(&reply);
819 }
820 
821 
822 void
823 LaunchDaemon::_HandleStopLaunchTarget(BMessage* message)
824 {
825 	uid_t user = _GetUserID(message);
826 	if (user < 0)
827 		return;
828 
829 	const char* name = message->GetString("target");
830 
831 	Target* target = FindTarget(name);
832 	if (target == NULL) {
833 		Session* session = FindSession(user);
834 		if (session != NULL) {
835 			// Forward request to user launch_daemon
836 			if (session->Daemon().SendMessage(message) == B_OK)
837 				return;
838 		}
839 
840 		BMessage reply(B_NAME_NOT_FOUND);
841 		message->SendReply(&reply);
842 		return;
843 	}
844 
845 	BMessage data;
846 	if (message->FindMessage("data", &data) == B_OK)
847 		target->AddData(data.GetString("name"), data);
848 
849 	bool force = message->GetBool("force");
850 	fLog.JobStopped(target, force);
851 	_StopJobs(target, force);
852 
853 	BMessage reply((uint32)B_OK);
854 	message->SendReply(&reply);
855 }
856 
857 
858 void
859 LaunchDaemon::_HandleLaunchJob(BMessage* message)
860 {
861 	uid_t user = _GetUserID(message);
862 	if (user < 0)
863 		return;
864 
865 	const char* name = message->GetString("name");
866 
867 	Job* job = FindJob(name);
868 	if (job == NULL) {
869 		Session* session = FindSession(user);
870 		if (session != NULL) {
871 			// Forward request to user launch_daemon
872 			if (session->Daemon().SendMessage(message) == B_OK)
873 				return;
874 		}
875 
876 		BMessage reply(B_NAME_NOT_FOUND);
877 		message->SendReply(&reply);
878 		return;
879 	}
880 
881 	job->SetEnabled(true);
882 	_LaunchJob(job, FORCE_NOW);
883 
884 	BMessage reply((uint32)B_OK);
885 	message->SendReply(&reply);
886 }
887 
888 
889 void
890 LaunchDaemon::_HandleEnableLaunchJob(BMessage* message)
891 {
892 	uid_t user = _GetUserID(message);
893 	if (user < 0)
894 		return;
895 
896 	const char* name = message->GetString("name");
897 	bool enable = message->GetBool("enable");
898 
899 	Job* job = FindJob(name);
900 	if (job == NULL) {
901 		Session* session = FindSession(user);
902 		if (session != NULL) {
903 			// Forward request to user launch_daemon
904 			if (session->Daemon().SendMessage(message) == B_OK)
905 				return;
906 		}
907 
908 		BMessage reply(B_NAME_NOT_FOUND);
909 		message->SendReply(&reply);
910 		return;
911 	}
912 
913 	job->SetEnabled(enable);
914 	fLog.JobEnabled(job, enable);
915 
916 	BMessage reply((uint32)B_OK);
917 	message->SendReply(&reply);
918 }
919 
920 
921 void
922 LaunchDaemon::_HandleStopLaunchJob(BMessage* message)
923 {
924 	uid_t user = _GetUserID(message);
925 	if (user < 0)
926 		return;
927 
928 	const char* name = message->GetString("name");
929 
930 	Job* job = FindJob(name);
931 	if (job == NULL) {
932 		Session* session = FindSession(user);
933 		if (session != NULL) {
934 			// Forward request to user launch_daemon
935 			if (session->Daemon().SendMessage(message) == B_OK)
936 				return;
937 		}
938 
939 		BMessage reply(B_NAME_NOT_FOUND);
940 		message->SendReply(&reply);
941 		return;
942 	}
943 
944 	bool force = message->GetBool("force");
945 	fLog.JobStopped(job, force);
946 	_StopJob(job, force);
947 
948 	BMessage reply((uint32)B_OK);
949 	message->SendReply(&reply);
950 }
951 
952 
953 void
954 LaunchDaemon::_HandleLaunchSession(BMessage* message)
955 {
956 	uid_t user = _GetUserID(message);
957 	if (user < 0)
958 		return;
959 
960 	status_t status = B_OK;
961 	const char* login = message->GetString("login");
962 	if (login == NULL)
963 		status = B_BAD_VALUE;
964 	if (status == B_OK && user != 0) {
965 		// Only the root user can start sessions
966 		// TODO: we'd actually need to know the uid of the sender
967 		status = B_PERMISSION_DENIED;
968 	}
969 	if (status == B_OK)
970 		status = _StartSession(login);
971 
972 	BMessage reply((uint32)status);
973 	message->SendReply(&reply);
974 }
975 
976 
977 void
978 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message)
979 {
980 	uid_t user = _GetUserID(message);
981 	if (user < 0)
982 		return;
983 
984 	status_t status = B_OK;
985 
986 	BMessenger target;
987 	if (message->FindMessenger("daemon", &target) != B_OK)
988 		status = B_BAD_VALUE;
989 
990 	if (status == B_OK) {
991 		Session* session = new (std::nothrow) Session(user, target);
992 		if (session != NULL)
993 			fSessions.insert(std::make_pair(user, session));
994 		else
995 			status = B_NO_MEMORY;
996 	}
997 
998 	BMessage reply((uint32)status);
999 	message->SendReply(&reply);
1000 }
1001 
1002 
1003 void
1004 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
1005 {
1006 	uid_t user = _GetUserID(message);
1007 	if (user < 0)
1008 		return;
1009 
1010 	if (user == 0 || fUserMode) {
1011 		status_t status = B_OK;
1012 
1013 		const char* name = message->GetString("name");
1014 		const char* ownerName = message->GetString("owner");
1015 		uint32 flags = message->GetUInt32("flags", 0);
1016 		BMessenger source;
1017 		if (name != NULL && ownerName != NULL
1018 			&& message->FindMessenger("source", &source) == B_OK) {
1019 			// Register event
1020 			ownerName = get_leaf(ownerName);
1021 
1022 			ExternalEventSource* event = new (std::nothrow)
1023 				ExternalEventSource(source, ownerName, name, flags);
1024 			if (event != NULL) {
1025 				// Use short name, and fully qualified name
1026 				BString eventName = name;
1027 				fEvents.insert(std::make_pair(eventName, event));
1028 				_ResolveExternalEvents(event, eventName);
1029 
1030 				eventName.Prepend("/");
1031 				eventName.Prepend(ownerName);
1032 				fEvents.insert(std::make_pair(eventName, event));
1033 				_ResolveExternalEvents(event, eventName);
1034 
1035 				fLog.ExternalEventRegistered(name);
1036 			} else
1037 				status = B_NO_MEMORY;
1038 		} else
1039 			status = B_BAD_VALUE;
1040 
1041 		BMessage reply((uint32)status);
1042 		message->SendReply(&reply);
1043 	}
1044 
1045 	_ForwardEventMessage(user, message);
1046 }
1047 
1048 
1049 void
1050 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message)
1051 {
1052 	uid_t user = _GetUserID(message);
1053 	if (user < 0)
1054 		return;
1055 
1056 	if (user == 0 || fUserMode) {
1057 		status_t status = B_OK;
1058 
1059 		const char* name = message->GetString("name");
1060 		const char* ownerName = message->GetString("owner");
1061 		BMessenger source;
1062 		if (name != NULL && ownerName != NULL
1063 			&& message->FindMessenger("source", &source) == B_OK) {
1064 			// Unregister short and fully qualified event name
1065 			ownerName = get_leaf(ownerName);
1066 
1067 			BString eventName = name;
1068 			fEvents.erase(eventName);
1069 
1070 			eventName.Prepend("/");
1071 			eventName.Prepend(ownerName);
1072 			fEvents.erase(eventName);
1073 
1074 			fLog.ExternalEventRegistered(name);
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 
1086 void
1087 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message)
1088 {
1089 	uid_t user = _GetUserID(message);
1090 	if (user < 0)
1091 		return;
1092 
1093 	if (user == 0 || fUserMode) {
1094 		// Trigger events
1095 		const char* name = message->GetString("name");
1096 		const char* ownerName = message->GetString("owner");
1097 		// TODO: support arguments (as selectors)
1098 
1099 		ExternalEventSource* event = _FindExternalEventSource(ownerName, name);
1100 		if (event != NULL) {
1101 			fLog.ExternalEventTriggered(name);
1102 			event->Trigger();
1103 		}
1104 	}
1105 
1106 	_ForwardEventMessage(user, message);
1107 }
1108 
1109 
1110 void
1111 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message)
1112 {
1113 	uid_t user = _GetUserID(message);
1114 	if (user < 0)
1115 		return;
1116 
1117 	if (user == 0 || fUserMode) {
1118 		// Reset sticky events
1119 		const char* name = message->GetString("name");
1120 		const char* ownerName = message->GetString("owner");
1121 		// TODO: support arguments (as selectors)
1122 
1123 		ExternalEventSource* eventSource = _FindExternalEventSource(ownerName, name);
1124 		if (eventSource != NULL)
1125 			eventSource->ResetSticky();
1126 	}
1127 
1128 	_ForwardEventMessage(user, message);
1129 }
1130 
1131 
1132 void
1133 LaunchDaemon::_HandleGetLaunchTargets(BMessage* message)
1134 {
1135 	uid_t user = _GetUserID(message);
1136 	if (user < 0)
1137 		return;
1138 
1139 	BMessage reply;
1140 	status_t status = B_OK;
1141 
1142 	if (!fUserMode) {
1143 		// Request the data from the user's daemon, too
1144 		Session* session = FindSession(user);
1145 		if (session != NULL) {
1146 			BMessage request(B_GET_LAUNCH_TARGETS);
1147 			status = request.AddInt32("user", 0);
1148 			if (status == B_OK) {
1149 				status = session->Daemon().SendMessage(&request,
1150 					&reply);
1151 			}
1152 			if (status == B_OK)
1153 				status = reply.what;
1154 		} else
1155 			status = B_NAME_NOT_FOUND;
1156 	}
1157 
1158 	if (status == B_OK) {
1159 		TargetMap::const_iterator iterator = fTargets.begin();
1160 		for (; iterator != fTargets.end(); iterator++)
1161 			reply.AddString("target", iterator->first);
1162 	}
1163 
1164 	reply.what = status;
1165 	message->SendReply(&reply);
1166 }
1167 
1168 
1169 void
1170 LaunchDaemon::_HandleGetLaunchTargetInfo(BMessage* message)
1171 {
1172 	uid_t user = _GetUserID(message);
1173 	if (user < 0)
1174 		return;
1175 
1176 	const char* name = message->GetString("name");
1177 	Target* target = FindTarget(name);
1178 	if (target == NULL && !fUserMode) {
1179 		_ForwardEventMessage(user, message);
1180 		return;
1181 	}
1182 
1183 	BMessage info(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND));
1184 	if (target != NULL) {
1185 		_GetBaseJobInfo(target, info);
1186 
1187 		for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1188 				iterator++) {
1189 			Job* job = iterator->second;
1190 			if (job->Target() == target)
1191 				info.AddString("job", job->Name());
1192 		}
1193 	}
1194 	message->SendReply(&info);
1195 }
1196 
1197 
1198 void
1199 LaunchDaemon::_HandleGetLaunchJobs(BMessage* message)
1200 {
1201 	uid_t user = _GetUserID(message);
1202 	if (user < 0)
1203 		return;
1204 
1205 	const char* targetName = message->GetString("target");
1206 
1207 	BMessage reply;
1208 	status_t status = B_OK;
1209 
1210 	if (!fUserMode) {
1211 		// Request the data from the user's daemon, too
1212 		Session* session = FindSession(user);
1213 		if (session != NULL) {
1214 			BMessage request(B_GET_LAUNCH_JOBS);
1215 			status = request.AddInt32("user", 0);
1216 			if (status == B_OK && targetName != NULL)
1217 				status = request.AddString("target", targetName);
1218 			if (status == B_OK) {
1219 				status = session->Daemon().SendMessage(&request,
1220 					&reply);
1221 			}
1222 			if (status == B_OK)
1223 				status = reply.what;
1224 		} else
1225 			status = B_NAME_NOT_FOUND;
1226 	}
1227 
1228 	if (status == B_OK) {
1229 		JobMap::const_iterator iterator = fJobs.begin();
1230 		for (; iterator != fJobs.end(); iterator++) {
1231 			Job* job = iterator->second;
1232 			if (targetName != NULL && (job->Target() == NULL
1233 					|| job->Target()->Title() != targetName)) {
1234 				continue;
1235 			}
1236 			reply.AddString("job", iterator->first);
1237 		}
1238 	}
1239 
1240 	reply.what = status;
1241 	message->SendReply(&reply);
1242 }
1243 
1244 
1245 void
1246 LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message)
1247 {
1248 	uid_t user = _GetUserID(message);
1249 	if (user < 0)
1250 		return;
1251 
1252 	const char* name = message->GetString("name");
1253 	Job* job = FindJob(name);
1254 	if (job == NULL && !fUserMode) {
1255 		_ForwardEventMessage(user, message);
1256 		return;
1257 	}
1258 
1259 	BMessage info(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND));
1260 	if (job != NULL) {
1261 		_GetBaseJobInfo(job, info);
1262 
1263 		info.SetInt32("team", job->Team());
1264 		info.SetBool("enabled", job->IsEnabled());
1265 		info.SetBool("running", job->IsRunning());
1266 		info.SetBool("launched", job->IsLaunched());
1267 		info.SetBool("service", job->IsService());
1268 
1269 		if (job->Target() != NULL)
1270 			info.SetString("target", job->Target()->Name());
1271 
1272 		for (int32 i = 0; i < job->Arguments().CountStrings(); i++)
1273 			info.AddString("launch", job->Arguments().StringAt(i));
1274 
1275 		for (int32 i = 0; i < job->Requirements().CountStrings(); i++)
1276 			info.AddString("requires", job->Requirements().StringAt(i));
1277 
1278 		PortMap::const_iterator iterator = job->Ports().begin();
1279 		for (; iterator != job->Ports().end(); iterator++)
1280 			info.AddMessage("port", &iterator->second);
1281 	}
1282 	message->SendReply(&info);
1283 }
1284 
1285 
1286 void
1287 LaunchDaemon::_HandleGetLaunchLog(BMessage* message)
1288 {
1289 	uid_t user = _GetUserID(message);
1290 	if (user < 0)
1291 		return;
1292 
1293 	BMessage filter;
1294 	BString jobName;
1295 	const char* event = NULL;
1296 	int32 limit = 0;
1297 	bool systemOnly = false;
1298 	bool userOnly = false;
1299 	if (message->FindMessage("filter", &filter) == B_OK) {
1300 		limit = filter.GetInt32("limit", 0);
1301 		jobName = filter.GetString("job");
1302 		jobName.ToLower();
1303 		event = filter.GetString("event");
1304 		systemOnly = filter.GetBool("systemOnly");
1305 		userOnly = filter.GetBool("userOnly");
1306 	}
1307 
1308 	BMessage info((uint32)B_OK);
1309 	int32 count = 0;
1310 
1311 	if (user == 0 || !userOnly) {
1312 		LogItemList::Iterator iterator = fLog.Iterator();
1313 		while (iterator.HasNext()) {
1314 			LogItem* item = iterator.Next();
1315 			if (!item->Matches(jobName.IsEmpty() ? NULL : jobName.String(),
1316 					event)) {
1317 				continue;
1318 			}
1319 
1320 			BMessage itemMessage;
1321 			itemMessage.AddUInt64("when", item->When());
1322 			itemMessage.AddInt32("type", (int32)item->Type());
1323 			itemMessage.AddString("message", item->Message());
1324 
1325 			BMessage parameter;
1326 			item->GetParameter(parameter);
1327 			itemMessage.AddMessage("parameter", &parameter);
1328 
1329 			info.AddMessage("item", &itemMessage);
1330 
1331 			// limit == 0 means no limit
1332 			if (++count == limit)
1333 				break;
1334 		}
1335 	}
1336 
1337 	// Get the list from the user daemon, and merge it into our reply
1338 	Session* session = FindSession(user);
1339 	if (session != NULL && !systemOnly) {
1340 		if (limit != 0) {
1341 			// Update limit for user daemon
1342 			limit -= count;
1343 			if (limit <= 0) {
1344 				message->SendReply(&info);
1345 				return;
1346 			}
1347 		}
1348 
1349 		BMessage reply;
1350 
1351 		BMessage request(B_GET_LAUNCH_LOG);
1352 		status_t status = request.AddInt32("user", 0);
1353 		if (status == B_OK && (limit != 0 || !jobName.IsEmpty()
1354 				|| event != NULL)) {
1355 			// Forward filter specification when needed
1356 			status = filter.SetInt32("limit", limit);
1357 			if (status == B_OK)
1358 				status = request.AddMessage("filter", &filter);
1359 		}
1360 		if (status == B_OK)
1361 			status = session->Daemon().SendMessage(&request, &reply);
1362 		if (status == B_OK)
1363 			info.AddMessage("user", &reply);
1364 	}
1365 
1366 	message->SendReply(&info);
1367 }
1368 
1369 
1370 uid_t
1371 LaunchDaemon::_GetUserID(BMessage* message)
1372 {
1373 	uid_t user = (uid_t)message->GetInt32("user", -1);
1374 	if (user < 0) {
1375 		BMessage reply((uint32)B_BAD_VALUE);
1376 		message->SendReply(&reply);
1377 	}
1378 	return user;
1379 }
1380 
1381 
1382 void
1383 LaunchDaemon::_ReadPaths(const BStringList& paths)
1384 {
1385 	for (int32 i = 0; i < paths.CountStrings(); i++) {
1386 		BEntry entry(paths.StringAt(i));
1387 		if (entry.InitCheck() != B_OK || !entry.Exists())
1388 			continue;
1389 
1390 		_ReadDirectory(NULL, entry);
1391 	}
1392 }
1393 
1394 
1395 void
1396 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry)
1397 {
1398 	if (entry.IsDirectory())
1399 		_ReadDirectory(context, entry);
1400 	else
1401 		_ReadFile(context, entry);
1402 }
1403 
1404 
1405 void
1406 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry)
1407 {
1408 	BDirectory directory(&directoryEntry);
1409 
1410 	BEntry entry;
1411 	while (directory.GetNextEntry(&entry) == B_OK) {
1412 		_ReadEntry(context, entry);
1413 	}
1414 }
1415 
1416 
1417 status_t
1418 LaunchDaemon::_ReadFile(const char* context, BEntry& entry)
1419 {
1420 	BPath path;
1421 	status_t status = path.SetTo(&entry);
1422 	if (status != B_OK)
1423 		return status;
1424 
1425 	SettingsParser parser;
1426 	BMessage message;
1427 	status = parser.ParseFile(path.Path(), message);
1428 	if (status == B_OK) {
1429 		TRACE("launch_daemon: read file %s\n", path.Path());
1430 		_AddJobs(NULL, message);
1431 		_AddTargets(message);
1432 		_AddRunTargets(message);
1433 	}
1434 
1435 	return status;
1436 }
1437 
1438 
1439 void
1440 LaunchDaemon::_AddJobs(Target* target, BMessage& message)
1441 {
1442 	BMessage job;
1443 	for (int32 index = 0; message.FindMessage("service", index,
1444 			&job) == B_OK; index++) {
1445 		_AddJob(target, true, job);
1446 	}
1447 
1448 	for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK;
1449 			index++) {
1450 		_AddJob(target, false, job);
1451 	}
1452 }
1453 
1454 
1455 void
1456 LaunchDaemon::_AddTargets(BMessage& message)
1457 {
1458 	BMessage targetMessage;
1459 	for (int32 index = 0; message.FindMessage("target", index,
1460 			&targetMessage) == B_OK; index++) {
1461 		const char* name = targetMessage.GetString("name");
1462 		if (name == NULL) {
1463 			// TODO: log error
1464 			debug_printf("Target has no name, ignoring it!\n");
1465 			continue;
1466 		}
1467 
1468 		Target* target = FindTarget(name);
1469 		if (target == NULL) {
1470 			target = new Target(name);
1471 			_AddTarget(target);
1472 		} else if (targetMessage.GetBool("reset")) {
1473 			// Remove all jobs from this target
1474 			for (JobMap::iterator iterator = fJobs.begin();
1475 					iterator != fJobs.end();) {
1476 				Job* job = iterator->second;
1477 				JobMap::iterator remove = iterator++;
1478 
1479 				if (job->Target() == target) {
1480 					fJobs.erase(remove);
1481 					delete job;
1482 				}
1483 			}
1484 		}
1485 
1486 		_SetCondition(target, targetMessage);
1487 		_SetEvent(target, targetMessage);
1488 		_SetEnvironment(target, targetMessage);
1489 		_AddJobs(target, targetMessage);
1490 
1491 		if (target->Event() != NULL)
1492 			target->Event()->Register(*this);
1493 	}
1494 }
1495 
1496 
1497 void
1498 LaunchDaemon::_AddRunTargets(BMessage& message)
1499 {
1500 	BMessage runMessage;
1501 	for (int32 index = 0; message.FindMessage("run", index,
1502 			&runMessage) == B_OK; index++) {
1503 		BMessage conditions;
1504 		bool pass = true;
1505 		if (runMessage.FindMessage("if", &conditions) == B_OK) {
1506 			Condition* condition = Conditions::FromMessage(conditions);
1507 			if (condition != NULL) {
1508 				pass = condition->Test(*this);
1509 				debug_printf("Test: %s -> %d\n", condition->ToString().String(),
1510 					pass);
1511 				delete condition;
1512 			} else
1513 				debug_printf("Could not parse condition!\n");
1514 		}
1515 
1516 		if (pass) {
1517 			_AddRunTargets(runMessage, NULL);
1518 			_AddRunTargets(runMessage, "then");
1519 		} else {
1520 			_AddRunTargets(runMessage, "else");
1521 		}
1522 	}
1523 }
1524 
1525 
1526 void
1527 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name)
1528 {
1529 	BMessage targets;
1530 	if (name != NULL && message.FindMessage(name, &targets) != B_OK)
1531 		return;
1532 
1533 	const char* target;
1534 	for (int32 index = 0; targets.FindString("target", index, &target) == B_OK;
1535 			index++) {
1536 		fRunTargets.Add(target);
1537 	}
1538 }
1539 
1540 
1541 void
1542 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message)
1543 {
1544 	BString name = message.GetString("name");
1545 	if (name.IsEmpty()) {
1546 		// Invalid job description
1547 		return;
1548 	}
1549 	name.ToLower();
1550 
1551 	Job* job = FindJob(name);
1552 	if (job == NULL) {
1553 		TRACE("  add job \"%s\"\n", name.String());
1554 
1555 		job = new (std::nothrow) Job(name);
1556 		if (job == NULL)
1557 			return;
1558 
1559 		job->SetTeamListener(this);
1560 		job->SetService(service);
1561 		job->SetCreateDefaultPort(service);
1562 		job->SetTarget(target);
1563 	} else
1564 		TRACE("  amend job \"%s\"\n", name.String());
1565 
1566 	if (message.HasBool("disabled")) {
1567 		job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled()));
1568 		fLog.JobEnabled(job, job->IsEnabled());
1569 	}
1570 
1571 	if (message.HasBool("legacy"))
1572 		job->SetCreateDefaultPort(!message.GetBool("legacy", !service));
1573 
1574 	_SetCondition(job, message);
1575 	_SetEvent(job, message);
1576 	_SetEnvironment(job, message);
1577 
1578 	BMessage portMessage;
1579 	for (int32 index = 0;
1580 			message.FindMessage("port", index, &portMessage) == B_OK; index++) {
1581 		job->AddPort(portMessage);
1582 	}
1583 
1584 	if (message.HasString("launch"))
1585 		message.FindStrings("launch", &job->Arguments());
1586 
1587 	const char* requirement;
1588 	for (int32 index = 0;
1589 			message.FindString("requires", index, &requirement) == B_OK;
1590 			index++) {
1591 		job->AddRequirement(requirement);
1592 	}
1593 	if (fInitTarget != NULL)
1594 		job->AddRequirement(fInitTarget->Name());
1595 
1596 	fJobs.insert(std::make_pair(job->Title(), job));
1597 }
1598 
1599 
1600 /*!	Initializes all jobs for the specified target (may be \c NULL).
1601 	Jobs that cannot be initialized, and those that never will be due to
1602 	conditions, will be removed from the list.
1603 */
1604 void
1605 LaunchDaemon::_InitJobs(Target* target)
1606 {
1607 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) {
1608 		Job* job = iterator->second;
1609 		JobMap::iterator remove = iterator++;
1610 
1611 		if (job->Target() != target)
1612 			continue;
1613 
1614 		status_t status = B_NO_INIT;
1615 		if (job->IsEnabled()) {
1616 			// Filter out jobs that have a constant and failing condition
1617 			if (job->Condition() == NULL || !job->Condition()->IsConstant(*this)
1618 				|| job->Condition()->Test(*this)) {
1619 				std::set<BString> dependencies;
1620 				status = job->Init(*this, dependencies);
1621 				if (status == B_OK && job->Event() != NULL)
1622 					status = job->Event()->Register(*this);
1623 			}
1624 		}
1625 
1626 		if (status == B_OK) {
1627 			fLog.JobInitialized(job);
1628 		} else {
1629 			if (status != B_NO_INIT) {
1630 				// TODO: log error
1631 				debug_printf("Init \"%s\" failed: %s\n", job->Name(),
1632 					strerror(status));
1633 			}
1634 			fLog.JobIgnored(job, status);
1635 
1636 			// Remove jobs that won't be used later on
1637 			fJobs.erase(remove);
1638 			delete job;
1639 		}
1640 	}
1641 }
1642 
1643 
1644 /*!	Adds all jobs for the specified target (may be \c NULL) to the launch
1645 	queue, except those that are triggered by events that haven't been
1646 	triggered yet.
1647 
1648 	Unless \a forceNow is true, the target is only launched if its events,
1649 	if any, have been triggered already, and its conditions are met.
1650 */
1651 void
1652 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow)
1653 {
1654 	if (!forceNow && target != NULL && (!target->EventHasTriggered()
1655 		|| !target->CheckCondition(*this))) {
1656 		return;
1657 	}
1658 
1659 	if (target != NULL && !target->HasLaunched()) {
1660 		target->SetLaunched(true);
1661 		_InitJobs(target);
1662 	}
1663 
1664 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1665 			iterator++) {
1666 		Job* job = iterator->second;
1667 		if (job->Target() == target)
1668 			_LaunchJob(job);
1669 	}
1670 }
1671 
1672 
1673 /*!	Stops all running jobs of the specified target (may be \c NULL).
1674 */
1675 void
1676 LaunchDaemon::_StopJobs(Target* target, bool force)
1677 {
1678 	if (target != NULL && !target->HasLaunched())
1679 		return;
1680 
1681 	for (JobMap::reverse_iterator iterator = fJobs.rbegin();
1682 			iterator != fJobs.rend(); iterator++) {
1683 		Job* job = iterator->second;
1684 		if (job->Target() == target)
1685 			_StopJob(job, force);
1686 	}
1687 }
1688 
1689 
1690 /*!	Checks whether or not the specified \a job can be launched.
1691 	If \a testOnly is \c false, calling this method will trigger a demand
1692 	to the \a job.
1693 */
1694 bool
1695 LaunchDaemon::_CanLaunchJob(Job* job, uint32 options, bool testOnly)
1696 {
1697 	if (job == NULL || !job->CanBeLaunched())
1698 		return false;
1699 
1700 	return (options & FORCE_NOW) != 0
1701 		|| (job->EventHasTriggered() && job->CheckCondition(*this)
1702 			&& ((options & TRIGGER_DEMAND) == 0
1703 				|| Events::TriggerDemand(job->Event(), testOnly)));
1704 }
1705 
1706 
1707 /*!	Checks recursively if the requirements of the specified job can be launched,
1708 	if they are not running already.
1709 	Calling this method will not trigger a demand for the requirements.
1710 */
1711 bool
1712 LaunchDaemon::_CanLaunchJobRequirements(Job* job, uint32 options)
1713 {
1714 	int32 count = job->Requirements().CountStrings();
1715 	for (int32 index = 0; index < count; index++) {
1716 		Job* requirement = FindJob(job->Requirements().StringAt(index));
1717 		if (requirement != NULL
1718 			&& !requirement->IsRunning() && !requirement->IsLaunching()
1719 			&& (!_CanLaunchJob(requirement, options, true)
1720 				|| _CanLaunchJobRequirements(requirement, options))) {
1721 			requirement->AddPending(job->Name());
1722 			return false;
1723 		}
1724 	}
1725 
1726 	return true;
1727 }
1728 
1729 
1730 /*!	Adds the specified \a job to the launch queue
1731 	queue, except those that are triggered by events.
1732 
1733 	Unless \c FORCE_NOW is set, the target is only launched if its events,
1734 	if any, have been triggered already.
1735 
1736 	Calling this method will trigger a demand event if \c TRIGGER_DEMAND has
1737 	been set.
1738 */
1739 bool
1740 LaunchDaemon::_LaunchJob(Job* job, uint32 options)
1741 {
1742 	if (job != NULL && (job->IsLaunching() || job->IsRunning()))
1743 		return true;
1744 
1745 	if (!_CanLaunchJob(job, options))
1746 		return false;
1747 
1748 	// Test if we can launch all requirements
1749 	if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND))
1750 		return false;
1751 
1752 	// Actually launch the requirements
1753 	int32 count = job->Requirements().CountStrings();
1754 	for (int32 index = 0; index < count; index++) {
1755 		Job* requirement = FindJob(job->Requirements().StringAt(index));
1756 		if (requirement != NULL) {
1757 			// TODO: For jobs that have their communication channels set up,
1758 			// we would not need to trigger demand at this point
1759 			if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) {
1760 				// Failed to put a requirement into the launch queue
1761 				return false;
1762 			}
1763 		}
1764 	}
1765 
1766 	if (job->Target() != NULL)
1767 		job->Target()->ResolveSourceFiles();
1768 	if (job->Event() != NULL)
1769 		job->Event()->ResetTrigger();
1770 
1771 	job->SetLaunching(true);
1772 
1773 	status_t status = fJobQueue.AddJob(job);
1774 	if (status != B_OK) {
1775 		debug_printf("Adding job %s to queue failed: %s\n", job->Name(),
1776 			strerror(status));
1777 		return false;
1778 	}
1779 
1780 	// Try to launch pending jobs as well
1781 	count = job->Pending().CountStrings();
1782 	for (int32 index = 0; index < count; index++) {
1783 		Job* pending = FindJob(job->Pending().StringAt(index));
1784 		if (pending != NULL && _LaunchJob(pending, 0)) {
1785 			// Remove the job from the pending list once its in the launch
1786 			// queue, so that is not being launched again next time.
1787 			index--;
1788 			count--;
1789 		}
1790 	}
1791 
1792 	return true;
1793 }
1794 
1795 
1796 void
1797 LaunchDaemon::_StopJob(Job* job, bool force)
1798 {
1799 	// TODO: find out which jobs require this job, and don't stop if any,
1800 	// unless force, and then stop them all.
1801 	job->SetEnabled(false);
1802 
1803 	if (!job->IsRunning())
1804 		return;
1805 
1806 	// Be nice first, and send a simple quit message
1807 	BMessenger messenger;
1808 	if (job->GetMessenger(messenger) == B_OK) {
1809 		BMessage request(B_QUIT_REQUESTED);
1810 		messenger.SendMessage(&request);
1811 
1812 		// TODO: wait a bit before going further
1813 		return;
1814 	}
1815 	// TODO: allow custom shutdown
1816 
1817 	send_signal(-job->Team(), SIGINT);
1818 	// TODO: this would be the next step, again, after a delay
1819 	//send_signal(job->Team(), SIGKILL);
1820 }
1821 
1822 
1823 void
1824 LaunchDaemon::_AddTarget(Target* target)
1825 {
1826 	fTargets.insert(std::make_pair(target->Title(), target));
1827 }
1828 
1829 
1830 void
1831 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message)
1832 {
1833 	Condition* condition = job->Condition();
1834 	bool updated = false;
1835 
1836 	BMessage conditions;
1837 	if (message.FindMessage("if", &conditions) == B_OK) {
1838 		condition = Conditions::FromMessage(conditions);
1839 		updated = true;
1840 	}
1841 
1842 	if (message.GetBool("no_safemode")) {
1843 		condition = Conditions::AddNotSafeMode(condition);
1844 		updated = true;
1845 	}
1846 
1847 	if (updated)
1848 		job->SetCondition(condition);
1849 }
1850 
1851 
1852 void
1853 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message)
1854 {
1855 	Event* event = job->Event();
1856 	bool updated = false;
1857 
1858 	BMessage events;
1859 	if (message.FindMessage("on", &events) == B_OK) {
1860 		event = Events::FromMessage(this, events);
1861 		updated = true;
1862 	}
1863 
1864 	if (message.GetBool("on_demand")) {
1865 		event = Events::AddOnDemand(this, event);
1866 		updated = true;
1867 	}
1868 
1869 	if (updated) {
1870 		TRACE("    event: %s\n", event->ToString().String());
1871 		job->SetEvent(event);
1872 	}
1873 }
1874 
1875 
1876 void
1877 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message)
1878 {
1879 	BMessage environmentMessage;
1880 	if (message.FindMessage("env", &environmentMessage) == B_OK)
1881 		job->SetEnvironment(environmentMessage);
1882 }
1883 
1884 
1885 ExternalEventSource*
1886 LaunchDaemon::_FindExternalEventSource(const char* owner, const char* name) const
1887 {
1888 	if (name == NULL)
1889 		return NULL;
1890 
1891 	BString eventName = name;
1892 	eventName.ToLower();
1893 
1894 	EventMap::const_iterator found = fEvents.find(eventName);
1895 	if (found != fEvents.end())
1896 		return found->second;
1897 
1898 	if (owner == NULL)
1899 		return NULL;
1900 
1901 	eventName.Prepend("/");
1902 	eventName.Prepend(get_leaf(owner));
1903 
1904 	found = fEvents.find(eventName);
1905 	if (found != fEvents.end())
1906 		return found->second;
1907 
1908 	return NULL;
1909 }
1910 
1911 
1912 void
1913 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* eventSource,
1914 	const BString& name)
1915 {
1916 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1917 			iterator++) {
1918 		Event* externalEvent = Events::ResolveExternalEvent(iterator->second->Event(),
1919 			name, eventSource->Flags());
1920 		if (externalEvent != NULL)
1921 			eventSource->AddDestination(externalEvent);
1922 	}
1923 }
1924 
1925 
1926 void
1927 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info)
1928 {
1929 	info.SetString("name", job->Name());
1930 
1931 	if (job->Event() != NULL)
1932 		info.SetString("event", job->Event()->ToString());
1933 
1934 	if (job->Condition() != NULL)
1935 		info.SetString("condition", job->Condition()->ToString());
1936 }
1937 
1938 
1939 void
1940 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message)
1941 {
1942 	if (fUserMode)
1943 		return;
1944 
1945 	// Forward event to user launch_daemon(s)
1946 	if (user == 0) {
1947 		for (SessionMap::iterator iterator = fSessions.begin();
1948 				iterator != fSessions.end(); iterator++) {
1949 			Session* session = iterator->second;
1950 			session->Daemon().SendMessage(message);
1951 				// ignore reply
1952 		}
1953 	} else {
1954 		Session* session = FindSession(user);
1955 		if (session != NULL)
1956 			session->Daemon().SendMessage(message);
1957 	}
1958 }
1959 
1960 
1961 status_t
1962 LaunchDaemon::_StartSession(const char* login)
1963 {
1964 	// TODO: enable user/group code
1965 	// The launch_daemon currently cannot talk to the registrar, though
1966 
1967 	struct passwd* passwd = getpwnam(login);
1968 	if (passwd == NULL)
1969 		return B_NAME_NOT_FOUND;
1970 	if (strcmp(passwd->pw_name, login) != 0)
1971 		return B_NAME_NOT_FOUND;
1972 
1973 	// Check if there is a user session running already
1974 	uid_t user = passwd->pw_uid;
1975 	gid_t group = passwd->pw_gid;
1976 
1977 	Unlock();
1978 
1979 	if (fork() == 0) {
1980 		if (setsid() < 0)
1981 			exit(EXIT_FAILURE);
1982 
1983 		if (initgroups(login, group) == -1)
1984 			exit(EXIT_FAILURE);
1985 		if (setgid(group) != 0)
1986 			exit(EXIT_FAILURE);
1987 		if (setuid(user) != 0)
1988 			exit(EXIT_FAILURE);
1989 
1990 		if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') {
1991 			setenv("HOME", passwd->pw_dir, true);
1992 
1993 			if (chdir(passwd->pw_dir) != 0) {
1994 				debug_printf("Could not switch to home dir %s: %s\n",
1995 					passwd->pw_dir, strerror(errno));
1996 			}
1997 		}
1998 
1999 		// TODO: This leaks the parent application
2000 		be_app = NULL;
2001 
2002 		// Reinitialize be_roster
2003 		BRoster::Private().DeleteBeRoster();
2004 		BRoster::Private().InitBeRoster();
2005 
2006 		// TODO: take over system jobs, and reserve their names (or ask parent)
2007 		status_t status;
2008 		LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status);
2009 		if (status == B_OK)
2010 			daemon->Run();
2011 
2012 		delete daemon;
2013 		exit(EXIT_SUCCESS);
2014 	}
2015 	Lock();
2016 	return B_OK;
2017 }
2018 
2019 
2020 void
2021 LaunchDaemon::_RetrieveKernelOptions()
2022 {
2023 	char buffer[32];
2024 	size_t size = sizeof(buffer);
2025 	status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer,
2026 		&size);
2027 	if (status == B_OK) {
2028 		fSafeMode = !strncasecmp(buffer, "true", size)
2029 			|| !strncasecmp(buffer, "yes", size)
2030 			|| !strncasecmp(buffer, "on", size)
2031 			|| !strncasecmp(buffer, "enabled", size);
2032 	} else
2033 		fSafeMode = false;
2034 }
2035 
2036 
2037 void
2038 LaunchDaemon::_SetupEnvironment()
2039 {
2040 	// Determine safemode kernel option
2041 	setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true);
2042 
2043 	// Default locale settings
2044 	setenv("LC_TYPE", "en_US.UTF-8", true);
2045 }
2046 
2047 
2048 /*!	Basic system initialization that must happen before any jobs are launched.
2049 */
2050 void
2051 LaunchDaemon::_InitSystem()
2052 {
2053 #ifndef TEST_MODE
2054 	_AddInitJob(new InitRealTimeClockJob());
2055 	_AddInitJob(new InitSharedMemoryDirectoryJob());
2056 	_AddInitJob(new InitTemporaryDirectoryJob());
2057 #endif
2058 
2059 	fJobQueue.AddJob(fInitTarget);
2060 }
2061 
2062 
2063 void
2064 LaunchDaemon::_AddInitJob(BJob* job)
2065 {
2066 	fInitTarget->AddDependency(job);
2067 	fJobQueue.AddJob(job);
2068 }
2069 
2070 
2071 // #pragma mark -
2072 
2073 
2074 #ifndef TEST_MODE
2075 
2076 
2077 static void
2078 open_stdio(int targetFD, int openMode)
2079 {
2080 #ifdef DEBUG
2081 	int fd = open("/dev/dprintf", openMode);
2082 #else
2083 	int fd = open("/dev/null", openMode);
2084 #endif
2085 	if (fd != targetFD) {
2086 		dup2(fd, targetFD);
2087 		close(fd);
2088 	}
2089 }
2090 
2091 
2092 #endif	// TEST_MODE
2093 
2094 
2095 int
2096 main()
2097 {
2098 	if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) {
2099 		fprintf(stderr, "The launch_daemon is already running!\n");
2100 		return EXIT_FAILURE;
2101 	}
2102 
2103 #ifndef TEST_MODE
2104 	// Make stdin/out/err available
2105 	open_stdio(STDIN_FILENO, O_RDONLY);
2106 	open_stdio(STDOUT_FILENO, O_WRONLY);
2107 	dup2(STDOUT_FILENO, STDERR_FILENO);
2108 #endif
2109 
2110 	EventMap events;
2111 	status_t status;
2112 	LaunchDaemon* daemon = new LaunchDaemon(false, events, status);
2113 	if (status == B_OK)
2114 		daemon->Run();
2115 
2116 	delete daemon;
2117 	return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;
2118 }
2119