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