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