xref: /haiku/src/servers/launch/LaunchDaemon.cpp (revision 236e68efdc9eed77818832b42d9b02161b21395d)
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 			return false;
1467 		}
1468 	}
1469 
1470 	return true;
1471 }
1472 
1473 
1474 /*!	Adds the specified \a job to the launch queue
1475 	queue, except those that are triggered by events.
1476 
1477 	Unless \c FORCE_NOW is set, the target is only launched if its events,
1478 	if any, have been triggered already.
1479 
1480 	Calling this method will trigger a demand event if \c TRIGGER_DEMAND has
1481 	been set.
1482 */
1483 bool
1484 LaunchDaemon::_LaunchJob(Job* job, uint32 options)
1485 {
1486 	if (job != NULL && (job->IsLaunching() || job->IsRunning()))
1487 		return true;
1488 
1489 	if (!_CanLaunchJob(job, options))
1490 		return false;
1491 
1492 	// Test if we can launch all requirements
1493 	if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND))
1494 		return false;
1495 
1496 	// Actually launch the requirements
1497 	int32 count = job->Requirements().CountStrings();
1498 	for (int32 index = 0; index < count; index++) {
1499 		Job* requirement = FindJob(job->Requirements().StringAt(index));
1500 		if (requirement != NULL) {
1501 			// TODO: For jobs that have their communication channels set up,
1502 			// we would not need to trigger demand at this point
1503 			if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) {
1504 				// Failed to put a requirement into the launch queue
1505 				return false;
1506 			}
1507 		}
1508 	}
1509 
1510 	if (job->Target() != NULL)
1511 		job->Target()->ResolveSourceFiles();
1512 	if (job->Event() != NULL)
1513 		job->Event()->ResetTrigger();
1514 
1515 	job->SetLaunching(true);
1516 
1517 	status_t status = fJobQueue.AddJob(job);
1518 	if (status != B_OK) {
1519 		debug_printf("Adding job %s to queue failed: %s\n", job->Name(),
1520 			strerror(status));
1521 		return false;
1522 	}
1523 
1524 	return true;
1525 }
1526 
1527 
1528 void
1529 LaunchDaemon::_StopJob(Job* job, bool force)
1530 {
1531 	// TODO: find out which jobs require this job, and don't stop if any,
1532 	// unless force, and then stop them all.
1533 	job->SetEnabled(false);
1534 
1535 	if (!job->IsRunning())
1536 		return;
1537 
1538 	// Be nice first, and send a simple quit message
1539 	BMessenger messenger;
1540 	if (job->GetMessenger(messenger) == B_OK) {
1541 		BMessage request(B_QUIT_REQUESTED);
1542 		messenger.SendMessage(&request);
1543 
1544 		// TODO: wait a bit before going further
1545 		return;
1546 	}
1547 	// TODO: allow custom shutdown
1548 
1549 	send_signal(-job->Team(), SIGINT);
1550 	// TODO: this would be the next step, again, after a delay
1551 	//send_signal(job->Team(), SIGKILL);
1552 }
1553 
1554 
1555 void
1556 LaunchDaemon::_AddTarget(Target* target)
1557 {
1558 	fTargets.insert(std::make_pair(target->Title(), target));
1559 }
1560 
1561 
1562 void
1563 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message)
1564 {
1565 	Condition* condition = job->Condition();
1566 	bool updated = false;
1567 
1568 	BMessage conditions;
1569 	if (message.FindMessage("if", &conditions) == B_OK) {
1570 		condition = Conditions::FromMessage(conditions);
1571 		updated = true;
1572 	}
1573 
1574 	if (message.GetBool("no_safemode")) {
1575 		condition = Conditions::AddNotSafeMode(condition);
1576 		updated = true;
1577 	}
1578 
1579 	if (updated)
1580 		job->SetCondition(condition);
1581 }
1582 
1583 
1584 void
1585 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message)
1586 {
1587 	Event* event = job->Event();
1588 	bool updated = false;
1589 
1590 	BMessage events;
1591 	if (message.FindMessage("on", &events) == B_OK) {
1592 		event = Events::FromMessage(this, events);
1593 		updated = true;
1594 	}
1595 
1596 	if (message.GetBool("on_demand")) {
1597 		event = Events::AddOnDemand(this, event);
1598 		updated = true;
1599 	}
1600 
1601 	if (updated) {
1602 		TRACE("    event: %s\n", event->ToString().String());
1603 		job->SetEvent(event);
1604 		_ResolveExternalEvents(job);
1605 	}
1606 }
1607 
1608 
1609 void
1610 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message)
1611 {
1612 	BMessage environmentMessage;
1613 	if (message.FindMessage("env", &environmentMessage) == B_OK)
1614 		job->SetEnvironment(environmentMessage);
1615 }
1616 
1617 
1618 ExternalEventSource*
1619 LaunchDaemon::_FindEvent(const char* owner, const char* name) const
1620 {
1621 	if (name == NULL)
1622 		return NULL;
1623 
1624 	BString eventName = name;
1625 	eventName.ToLower();
1626 
1627 	EventMap::const_iterator found = fEvents.find(eventName);
1628 	if (found != fEvents.end())
1629 		return found->second;
1630 
1631 	if (owner == NULL)
1632 		return NULL;
1633 
1634 	eventName.Prepend("/");
1635 	eventName.Prepend(get_leaf(owner));
1636 
1637 	found = fEvents.find(eventName);
1638 	if (found != fEvents.end())
1639 		return found->second;
1640 
1641 	return NULL;
1642 }
1643 
1644 
1645 void
1646 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event,
1647 	const BString& name)
1648 {
1649 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1650 			iterator++) {
1651 		Job* job = iterator->second;
1652 		if (Events::ResolveExternalEvent(job->Event(), name, event->Flags()))
1653 			event->AddListener(job);
1654 	}
1655 }
1656 
1657 
1658 void
1659 LaunchDaemon::_ResolveExternalEvents(BaseJob* job)
1660 {
1661 	if (job->Event() == NULL)
1662 		return;
1663 
1664 	for (EventMap::iterator iterator = fEvents.begin();
1665 			iterator != fEvents.end(); iterator++) {
1666 		ExternalEventSource* event = iterator->second;
1667 		if (Events::ResolveExternalEvent(job->Event(), event->Name(),
1668 				event->Flags()))
1669 			event->AddListener(job);
1670 	}
1671 }
1672 
1673 
1674 void
1675 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info)
1676 {
1677 	info.SetString("name", job->Name());
1678 
1679 	if (job->Event() != NULL)
1680 		info.SetString("event", job->Event()->ToString());
1681 
1682 	if (job->Condition() != NULL)
1683 		info.SetString("condition", job->Condition()->ToString());
1684 }
1685 
1686 
1687 void
1688 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message)
1689 {
1690 	if (fUserMode)
1691 		return;
1692 
1693 	// Forward event to user launch_daemon(s)
1694 	if (user == 0) {
1695 		for (SessionMap::iterator iterator = fSessions.begin();
1696 				iterator != fSessions.end(); iterator++) {
1697 			Session* session = iterator->second;
1698 			session->Daemon().SendMessage(message);
1699 				// ignore reply
1700 		}
1701 	} else {
1702 		Session* session = FindSession(user);
1703 		if (session != NULL)
1704 			session->Daemon().SendMessage(message);
1705 	}
1706 }
1707 
1708 
1709 status_t
1710 LaunchDaemon::_StartSession(const char* login)
1711 {
1712 	// TODO: enable user/group code
1713 	// The launch_daemon currently cannot talk to the registrar, though
1714 
1715 	struct passwd* passwd = getpwnam(login);
1716 	if (passwd == NULL)
1717 		return B_NAME_NOT_FOUND;
1718 	if (strcmp(passwd->pw_name, login) != 0)
1719 		return B_NAME_NOT_FOUND;
1720 
1721 	// Check if there is a user session running already
1722 	uid_t user = passwd->pw_uid;
1723 	gid_t group = passwd->pw_gid;
1724 
1725 	Unlock();
1726 
1727 	if (fork() == 0) {
1728 		if (setsid() < 0)
1729 			exit(EXIT_FAILURE);
1730 
1731 		if (initgroups(login, group) == -1)
1732 			exit(EXIT_FAILURE);
1733 		if (setgid(group) != 0)
1734 			exit(EXIT_FAILURE);
1735 		if (setuid(user) != 0)
1736 			exit(EXIT_FAILURE);
1737 
1738 		if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') {
1739 			setenv("HOME", passwd->pw_dir, true);
1740 
1741 			if (chdir(passwd->pw_dir) != 0) {
1742 				debug_printf("Could not switch to home dir %s: %s\n",
1743 					passwd->pw_dir, strerror(errno));
1744 			}
1745 		}
1746 
1747 		// TODO: This leaks the parent application
1748 		be_app = NULL;
1749 
1750 		// Reinitialize be_roster
1751 		BRoster::Private().DeleteBeRoster();
1752 		BRoster::Private().InitBeRoster();
1753 
1754 		// TODO: take over system jobs, and reserve their names (or ask parent)
1755 		status_t status;
1756 		LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status);
1757 		if (status == B_OK)
1758 			daemon->Run();
1759 
1760 		delete daemon;
1761 		exit(EXIT_SUCCESS);
1762 	}
1763 	Lock();
1764 	return B_OK;
1765 }
1766 
1767 
1768 void
1769 LaunchDaemon::_RetrieveKernelOptions()
1770 {
1771 	char buffer[32];
1772 	size_t size = sizeof(buffer);
1773 	status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer,
1774 		&size);
1775 	if (status == B_OK) {
1776 		fSafeMode = !strncasecmp(buffer, "true", size)
1777 			|| !strncasecmp(buffer, "yes", size)
1778 			|| !strncasecmp(buffer, "on", size)
1779 			|| !strncasecmp(buffer, "enabled", size);
1780 	} else
1781 		fSafeMode = false;
1782 }
1783 
1784 
1785 void
1786 LaunchDaemon::_SetupEnvironment()
1787 {
1788 	// Determine safemode kernel option
1789 	setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true);
1790 
1791 	// Default locale settings
1792 	setenv("LC_TYPE", "en_US.UTF-8", true);
1793 }
1794 
1795 
1796 /*!	Basic system initialization that must happen before any jobs are launched.
1797 */
1798 void
1799 LaunchDaemon::_InitSystem()
1800 {
1801 	_AddInitJob(new InitRealTimeClockJob());
1802 	_AddInitJob(new InitSharedMemoryDirectoryJob());
1803 	_AddInitJob(new InitTemporaryDirectoryJob());
1804 
1805 	fJobQueue.AddJob(fInitTarget);
1806 }
1807 
1808 
1809 void
1810 LaunchDaemon::_AddInitJob(BJob* job)
1811 {
1812 	fInitTarget->AddDependency(job);
1813 	fJobQueue.AddJob(job);
1814 }
1815 
1816 
1817 // #pragma mark -
1818 
1819 
1820 static void
1821 open_stdio(int targetFD, int openMode)
1822 {
1823 #ifdef DEBUG
1824 	int fd = open("/dev/dprintf", openMode);
1825 #else
1826 	int fd = open("/dev/null", openMode);
1827 #endif
1828 	if (fd != targetFD) {
1829 		dup2(fd, targetFD);
1830 		close(fd);
1831 	}
1832 }
1833 
1834 
1835 int
1836 main()
1837 {
1838 	// Make stdin/out/err available
1839 	open_stdio(STDIN_FILENO, O_RDONLY);
1840 	open_stdio(STDOUT_FILENO, O_WRONLY);
1841 	dup2(STDOUT_FILENO, STDERR_FILENO);
1842 
1843 	EventMap events;
1844 	status_t status;
1845 	LaunchDaemon* daemon = new LaunchDaemon(false, events, status);
1846 	if (status == B_OK)
1847 		daemon->Run();
1848 
1849 	delete daemon;
1850 	return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;
1851 }
1852