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