xref: /haiku/src/servers/launch/LaunchDaemon.cpp (revision 2e17b6504f199aa8954500b731cb7be9a0952279)
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 <RosterPrivate.h>
31 #include <syscalls.h>
32 
33 #include "multiuser_utils.h"
34 
35 #include "Conditions.h"
36 #include "Events.h"
37 #include "InitRealTimeClockJob.h"
38 #include "InitSharedMemoryDirectoryJob.h"
39 #include "InitTemporaryDirectoryJob.h"
40 #include "Job.h"
41 #include "SettingsParser.h"
42 #include "Target.h"
43 #include "Utility.h"
44 #include "Worker.h"
45 
46 
47 using namespace ::BPrivate;
48 using namespace BSupportKit;
49 using BSupportKit::BPrivate::JobQueue;
50 
51 
52 static const char* kLaunchDirectory = "launch";
53 static const char* kUserLaunchDirectory = "user_launch";
54 
55 
56 enum launch_options {
57 	FORCE_NOW		= 0x01,
58 	TRIGGER_DEMAND	= 0x02
59 };
60 
61 
62 class Session {
63 public:
64 								Session(uid_t user, const BMessenger& target);
65 
66 			uid_t				User() const
67 									{ return fUser; }
68 			const BMessenger&	Daemon() const
69 									{ return fDaemon; }
70 
71 private:
72 			uid_t				fUser;
73 			BMessenger			fDaemon;
74 };
75 
76 
77 /*!	This class is the connection between the external events that are part of
78 	a job, and the external event source.
79 
80 	There is one object per registered event source, and it keeps all jobs that
81 	reference the event as listeners. If the event source triggers the event,
82 	the object will be used to trigger the jobs.
83 */
84 class ExternalEventSource {
85 public:
86 								ExternalEventSource(BMessenger& source,
87 									const char* ownerName,
88 									const char* name, uint32 flags);
89 								~ExternalEventSource();
90 
91 			const char*			Name() const;
92 			uint32				Flags() const
93 									{ return fFlags; }
94 
95 			int32				CountListeners() const;
96 			BaseJob*			ListenerAt(int32 index) const;
97 
98 			status_t			AddListener(BaseJob* job);
99 			void				RemoveListener(BaseJob* job);
100 
101 private:
102 			BString				fName;
103 			uint32				fFlags;
104 			BObjectList<BaseJob> fListeners;
105 };
106 
107 
108 typedef std::map<BString, Job*> JobMap;
109 typedef std::map<uid_t, Session*> SessionMap;
110 typedef std::map<BString, Target*> TargetMap;
111 typedef std::map<BString, ExternalEventSource*> EventMap;
112 
113 
114 class LaunchDaemon : public BServer, public Finder, public ConditionContext,
115 	public EventRegistrator {
116 public:
117 								LaunchDaemon(bool userMode,
118 									const EventMap& events, status_t& error);
119 	virtual						~LaunchDaemon();
120 
121 	virtual	Job*				FindJob(const char* name) const;
122 	virtual	Target*				FindTarget(const char* name) const;
123 			Session*			FindSession(uid_t user) const;
124 
125 	// ConditionContext
126 	virtual	bool				IsSafeMode() const;
127 	virtual	bool				BootVolumeIsReadOnly() const;
128 
129 	// EventRegistrator
130 	virtual	status_t			RegisterExternalEvent(Event* event,
131 									const char* name,
132 									const BStringList& arguments);
133 	virtual	void				UnregisterExternalEvent(Event* event,
134 									const char* name);
135 
136 	virtual	void				ReadyToRun();
137 	virtual	void				MessageReceived(BMessage* message);
138 
139 private:
140 			void				_HandleGetLaunchData(BMessage* message);
141 			void				_HandleLaunchTarget(BMessage* message);
142 			void				_HandleLaunchSession(BMessage* message);
143 			void				_HandleRegisterSessionDaemon(BMessage* message);
144 			void				_HandleRegisterLaunchEvent(BMessage* message);
145 			void				_HandleUnregisterLaunchEvent(BMessage* message);
146 			void				_HandleNotifyLaunchEvent(BMessage* message);
147 			void				_HandleResetStickyLaunchEvent(
148 									BMessage* message);
149 			uid_t				_GetUserID(BMessage* message);
150 
151 			void				_ReadPaths(const BStringList& paths);
152 			void				_ReadEntry(const char* context, BEntry& entry);
153 			void				_ReadDirectory(const char* context,
154 									BEntry& directory);
155 			status_t			_ReadFile(const char* context, BEntry& entry);
156 
157 			void				_AddJobs(Target* target, BMessage& message);
158 			void				_AddTargets(BMessage& message);
159 			void				_AddRunTargets(BMessage& message);
160 			void				_AddRunTargets(BMessage& message,
161 									const char* name);
162 			void				_AddJob(Target* target, bool service,
163 									BMessage& message);
164 			void				_InitJobs(Target* target);
165 			void				_LaunchJobs(Target* target,
166 									bool forceNow = false);
167 			void				_LaunchJob(Job* job, uint32 options = 0);
168 			void				_AddTarget(Target* target);
169 			void				_SetCondition(BaseJob* job,
170 									const BMessage& message);
171 			void				_SetEvent(BaseJob* job,
172 									const BMessage& message);
173 			void				_SetEnvironment(BaseJob* job,
174 									const BMessage& message);
175 
176 			ExternalEventSource*
177 								_FindEvent(const char* owner,
178 									const char* name) const;
179 			void				_ResolveExternalEvents(
180 									ExternalEventSource* event,
181 									const BString& name);
182 			void				_ResolveExternalEvents(BaseJob* job);
183 			void				_ForwardEventMessage(uid_t user,
184 									BMessage* message);
185 
186 			status_t			_StartSession(const char* login);
187 
188 			void				_RetrieveKernelOptions();
189 			void				_SetupEnvironment();
190 			void				_InitSystem();
191 			void				_AddInitJob(BJob* job);
192 
193 private:
194 			JobMap				fJobs;
195 			TargetMap			fTargets;
196 			BStringList			fRunTargets;
197 			EventMap			fEvents;
198 			JobQueue			fJobQueue;
199 			SessionMap			fSessions;
200 			MainWorker*			fMainWorker;
201 			Target*				fInitTarget;
202 			bool				fSafeMode;
203 			bool				fReadOnlyBootVolume;
204 			bool				fUserMode;
205 };
206 
207 
208 static const char*
209 get_leaf(const char* signature)
210 {
211 	const char* separator = strrchr(signature, '/');
212 	if (separator != NULL)
213 		return separator + 1;
214 
215 	return signature;
216 }
217 
218 
219 // #pragma mark -
220 
221 
222 Session::Session(uid_t user, const BMessenger& daemon)
223 	:
224 	fUser(user),
225 	fDaemon(daemon)
226 {
227 }
228 
229 
230 // #pragma mark -
231 
232 
233 ExternalEventSource::ExternalEventSource(BMessenger& source,
234 	const char* ownerName, const char* name, uint32 flags)
235 	:
236 	fName(name),
237 	fFlags(flags),
238 	fListeners(5, true)
239 {
240 }
241 
242 
243 ExternalEventSource::~ExternalEventSource()
244 {
245 }
246 
247 
248 const char*
249 ExternalEventSource::Name() const
250 {
251 	return fName.String();
252 }
253 
254 
255 int32
256 ExternalEventSource::CountListeners() const
257 {
258 	return fListeners.CountItems();
259 }
260 
261 
262 BaseJob*
263 ExternalEventSource::ListenerAt(int32 index) const
264 {
265 	return fListeners.ItemAt(index);
266 }
267 
268 
269 status_t
270 ExternalEventSource::AddListener(BaseJob* job)
271 {
272 	if (fListeners.AddItem(job))
273 		return B_OK;
274 
275 	return B_NO_MEMORY;
276 }
277 
278 
279 void
280 ExternalEventSource::RemoveListener(BaseJob* job)
281 {
282 	fListeners.RemoveItem(job);
283 }
284 
285 
286 // #pragma mark -
287 
288 
289 LaunchDaemon::LaunchDaemon(bool userMode, const EventMap& events,
290 	status_t& error)
291 	:
292 	BServer(kLaunchDaemonSignature, NULL,
293 		create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
294 			userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error),
295 	fEvents(events),
296 	fInitTarget(userMode ? NULL : new Target("init")),
297 	fUserMode(userMode)
298 {
299 	fMainWorker = new MainWorker(fJobQueue);
300 	fMainWorker->Init();
301 
302 	if (fInitTarget != NULL)
303 		_AddTarget(fInitTarget);
304 
305 	// We may not be able to talk to the registrar
306 	if (!fUserMode)
307 		BRoster::Private().SetWithoutRegistrar(true);
308 }
309 
310 
311 LaunchDaemon::~LaunchDaemon()
312 {
313 }
314 
315 
316 Job*
317 LaunchDaemon::FindJob(const char* name) const
318 {
319 	if (name == NULL)
320 		return NULL;
321 
322 	JobMap::const_iterator found = fJobs.find(BString(name).ToLower());
323 	if (found != fJobs.end())
324 		return found->second;
325 
326 	return NULL;
327 }
328 
329 
330 Target*
331 LaunchDaemon::FindTarget(const char* name) const
332 {
333 	if (name == NULL)
334 		return NULL;
335 
336 	TargetMap::const_iterator found = fTargets.find(BString(name).ToLower());
337 	if (found != fTargets.end())
338 		return found->second;
339 
340 	return NULL;
341 }
342 
343 
344 Session*
345 LaunchDaemon::FindSession(uid_t user) const
346 {
347 	SessionMap::const_iterator found = fSessions.find(user);
348 	if (found != fSessions.end())
349 		return found->second;
350 
351 	return NULL;
352 }
353 
354 
355 bool
356 LaunchDaemon::IsSafeMode() const
357 {
358 	return fSafeMode;
359 }
360 
361 
362 bool
363 LaunchDaemon::BootVolumeIsReadOnly() const
364 {
365 	return fReadOnlyBootVolume;
366 }
367 
368 
369 status_t
370 LaunchDaemon::RegisterExternalEvent(Event* event, const char* name,
371 	const BStringList& arguments)
372 {
373 	// TODO: register actual event with event source
374 	return B_OK;
375 }
376 
377 
378 void
379 LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name)
380 {
381 	// TODO!
382 }
383 
384 
385 void
386 LaunchDaemon::ReadyToRun()
387 {
388 	_RetrieveKernelOptions();
389 	_SetupEnvironment();
390 
391 	fReadOnlyBootVolume = Utility::IsReadOnlyVolume("/boot");
392 	if (fReadOnlyBootVolume)
393 		Utility::BlockMedia("/boot", true);
394 
395 	if (fUserMode) {
396 		BLaunchRoster roster;
397 		BLaunchRoster::Private(roster).RegisterSessionDaemon(this);
398 	} else
399 		_InitSystem();
400 
401 	BStringList paths;
402 	if (fUserMode) {
403 		// System-wide user specific jobs
404 		BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kUserLaunchDirectory,
405 			B_FIND_PATHS_SYSTEM_ONLY, paths);
406 		_ReadPaths(paths);
407 	}
408 
409 	BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory,
410 		fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
411 	_ReadPaths(paths);
412 
413 	if (fUserMode) {
414 		BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY,
415 			kUserLaunchDirectory, B_FIND_PATHS_SYSTEM_ONLY, paths);
416 		_ReadPaths(paths);
417 	}
418 
419 	BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory,
420 		fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
421 	_ReadPaths(paths);
422 
423 	_InitJobs(NULL);
424 	_LaunchJobs(NULL);
425 
426 	// Launch run targets (ignores events)
427 	for (int32 index = 0; index < fRunTargets.CountStrings(); index++) {
428 		Target* target = FindTarget(fRunTargets.StringAt(index));
429 		if (target != NULL)
430 			_LaunchJobs(target);
431 	}
432 }
433 
434 
435 void
436 LaunchDaemon::MessageReceived(BMessage* message)
437 {
438 	switch (message->what) {
439 		case B_GET_LAUNCH_DATA:
440 			_HandleGetLaunchData(message);
441 			break;
442 
443 		case B_LAUNCH_TARGET:
444 			_HandleLaunchTarget(message);
445 			break;
446 
447 		case B_LAUNCH_SESSION:
448 			_HandleLaunchSession(message);
449 			break;
450 
451 		case B_REGISTER_SESSION_DAEMON:
452 			_HandleRegisterSessionDaemon(message);
453 			break;
454 
455 		case B_REGISTER_LAUNCH_EVENT:
456 			_HandleRegisterLaunchEvent(message);
457 			break;
458 
459 		case B_UNREGISTER_LAUNCH_EVENT:
460 			_HandleUnregisterLaunchEvent(message);
461 			break;
462 
463 		case B_NOTIFY_LAUNCH_EVENT:
464 			_HandleNotifyLaunchEvent(message);
465 			break;
466 
467 		case B_RESET_STICKY_LAUNCH_EVENT:
468 			_HandleResetStickyLaunchEvent(message);
469 			break;
470 
471 		case kMsgEventTriggered:
472 		{
473 			// An internal event has been triggered.
474 			// Check if its job can be launched now.
475 			const char* name = message->GetString("owner");
476 			if (name == NULL)
477 				break;
478 
479 			Job* job = FindJob(name);
480 			if (job != NULL) {
481 				_LaunchJob(job);
482 				break;
483 			}
484 
485 			Target* target = FindTarget(name);
486 			if (target != NULL) {
487 				_LaunchJobs(target);
488 				break;
489 			}
490 			break;
491 		}
492 
493 		default:
494 			BServer::MessageReceived(message);
495 			break;
496 	}
497 }
498 
499 
500 void
501 LaunchDaemon::_HandleGetLaunchData(BMessage* message)
502 {
503 	uid_t user = _GetUserID(message);
504 	if (user < 0)
505 		return;
506 
507 	BMessage reply((uint32)B_OK);
508 	bool launchJob = true;
509 
510 	Job* job = FindJob(get_leaf(message->GetString("name")));
511 	if (job == NULL) {
512 		Session* session = FindSession(user);
513 		if (session != NULL) {
514 			// Forward request to user launch_daemon
515 			if (session->Daemon().SendMessage(message) == B_OK)
516 				return;
517 		}
518 		reply.what = B_NAME_NOT_FOUND;
519 	} else if (job->IsService() && !job->IsLaunched()) {
520 		if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) {
521 			// The job exists, but cannot be started yet, as its
522 			// conditions are not met; don't make it available yet
523 			// TODO: we may not want to initialize jobs with conditions
524 			// that aren't met yet
525 			reply.what = B_NO_INIT;
526 		} else if (job->Event() != NULL) {
527 			if (!Events::TriggerDemand(job->Event())) {
528 				// The job is not triggered by demand; we cannot start it now
529 				reply.what = B_NO_INIT;
530 			} else {
531 				// The job has already been triggered, don't launch it again
532 				launchJob = false;
533 			}
534 		}
535 	} else
536 		launchJob = false;
537 
538 	bool ownsMessage = false;
539 	if (reply.what == B_OK) {
540 		// Launch the job if it hasn't been launched already
541 		if (launchJob)
542 			_LaunchJob(job, TRIGGER_DEMAND);
543 
544 		DetachCurrentMessage();
545 		status_t result = job->HandleGetLaunchData(message);
546 		if (result == B_OK) {
547 			// Replying is delegated to the job.
548 			return;
549 		}
550 
551 		ownsMessage = true;
552 		reply.what = result;
553 	}
554 
555 	message->SendReply(&reply);
556 	if (ownsMessage)
557 		delete message;
558 }
559 
560 
561 void
562 LaunchDaemon::_HandleLaunchTarget(BMessage* message)
563 {
564 	uid_t user = _GetUserID(message);
565 	if (user < 0)
566 		return;
567 
568 	const char* name = message->GetString("target");
569 	const char* baseName = message->GetString("base target");
570 
571 	Target* target = FindTarget(name);
572 	if (target == NULL && baseName != NULL) {
573 		Target* baseTarget = FindTarget(baseName);
574 		if (baseTarget != NULL) {
575 			target = new Target(name);
576 
577 			// Copy all jobs with the base target into the new target
578 			for (JobMap::iterator iterator = fJobs.begin();
579 					iterator != fJobs.end();) {
580 				Job* job = iterator->second;
581 				iterator++;
582 
583 				if (job->Target() == baseTarget) {
584 					Job* copy = new Job(*job);
585 					copy->SetTarget(target);
586 
587 					fJobs.insert(std::make_pair(copy->Name(), copy));
588 				}
589 			}
590 		}
591 	}
592 	if (target == NULL) {
593 		Session* session = FindSession(user);
594 		if (session != NULL) {
595 			// Forward request to user launch_daemon
596 			if (session->Daemon().SendMessage(message) == B_OK)
597 				return;
598 		}
599 
600 		BMessage reply(B_NAME_NOT_FOUND);
601 		message->SendReply(&reply);
602 		return;
603 	}
604 
605 	BMessage data;
606 	if (message->FindMessage("data", &data) == B_OK)
607 		target->AddData(data.GetString("name"), data);
608 
609 	_LaunchJobs(target);
610 }
611 
612 
613 void
614 LaunchDaemon::_HandleLaunchSession(BMessage* message)
615 {
616 	uid_t user = _GetUserID(message);
617 	if (user < 0)
618 		return;
619 
620 	status_t status = B_OK;
621 	const char* login = message->GetString("login");
622 	if (login == NULL)
623 		status = B_BAD_VALUE;
624 	if (status == B_OK && user != 0) {
625 		// Only the root user can start sessions
626 		// TODO: we'd actually need to know the uid of the sender
627 		status = B_PERMISSION_DENIED;
628 	}
629 	if (status == B_OK)
630 		status = _StartSession(login);
631 
632 	BMessage reply((uint32)status);
633 	message->SendReply(&reply);
634 }
635 
636 
637 void
638 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message)
639 {
640 	uid_t user = _GetUserID(message);
641 	if (user < 0)
642 		return;
643 
644 	status_t status = B_OK;
645 
646 	BMessenger target;
647 	if (message->FindMessenger("daemon", &target) != B_OK)
648 		status = B_BAD_VALUE;
649 
650 	if (status == B_OK) {
651 		Session* session = new (std::nothrow) Session(user, target);
652 		if (session != NULL)
653 			fSessions.insert(std::make_pair(user, session));
654 		else
655 			status = B_NO_MEMORY;
656 	}
657 
658 	BMessage reply((uint32)status);
659 	message->SendReply(&reply);
660 }
661 
662 
663 void
664 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
665 {
666 	uid_t user = _GetUserID(message);
667 	if (user < 0)
668 		return;
669 
670 	if (user == 0 || fUserMode) {
671 		status_t status = B_OK;
672 
673 		const char* name = message->GetString("name");
674 		const char* ownerName = message->GetString("owner");
675 		uint32 flags = message->GetUInt32("flags", 0);
676 		BMessenger source;
677 		if (name != NULL && ownerName != NULL
678 			&& message->FindMessenger("source", &source) == B_OK) {
679 			// Register event
680 			ownerName = get_leaf(ownerName);
681 
682 			ExternalEventSource* event = new (std::nothrow)
683 				ExternalEventSource(source, ownerName, name, flags);
684 			if (event != NULL) {
685 				// Use short name, and fully qualified name
686 				BString eventName = name;
687 				fEvents.insert(std::make_pair(eventName, event));
688 				_ResolveExternalEvents(event, eventName);
689 
690 				eventName.Prepend("/");
691 				eventName.Prepend(ownerName);
692 				fEvents.insert(std::make_pair(eventName, event));
693 				_ResolveExternalEvents(event, eventName);
694 			} else
695 				status = B_NO_MEMORY;
696 		} else
697 			status = B_BAD_VALUE;
698 
699 		BMessage reply((uint32)status);
700 		message->SendReply(&reply);
701 	}
702 
703 	_ForwardEventMessage(user, message);
704 }
705 
706 
707 void
708 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message)
709 {
710 	uid_t user = _GetUserID(message);
711 	if (user < 0)
712 		return;
713 
714 	if (user == 0 || fUserMode) {
715 		status_t status = B_OK;
716 
717 		const char* name = message->GetString("name");
718 		const char* ownerName = message->GetString("owner");
719 		BMessenger source;
720 		if (name != NULL && ownerName != NULL
721 			&& message->FindMessenger("source", &source) == B_OK) {
722 			// Unregister short and fully qualified event name
723 			ownerName = get_leaf(ownerName);
724 
725 			BString eventName = name;
726 			fEvents.erase(eventName);
727 
728 			eventName.Prepend("/");
729 			eventName.Prepend(ownerName);
730 			fEvents.erase(eventName);
731 		} else
732 			status = B_BAD_VALUE;
733 
734 		BMessage reply((uint32)status);
735 		message->SendReply(&reply);
736 	}
737 
738 	_ForwardEventMessage(user, message);
739 }
740 
741 
742 void
743 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message)
744 {
745 	uid_t user = _GetUserID(message);
746 	if (user < 0)
747 		return;
748 
749 	if (user == 0 || fUserMode) {
750 		// Trigger events
751 		const char* name = message->GetString("name");
752 		const char* ownerName = message->GetString("owner");
753 		// TODO: support arguments (as selectors)
754 
755 		ExternalEventSource* event = _FindEvent(ownerName, name);
756 		if (event != NULL) {
757 			// Evaluate all of its jobs
758 			int32 count = event->CountListeners();
759 			for (int32 index = 0; index < count; index++) {
760 				BaseJob* listener = event->ListenerAt(index);
761 				Events::TriggerExternalEvent(listener->Event(), name);
762 			}
763 		}
764 	}
765 
766 	_ForwardEventMessage(user, message);
767 }
768 
769 
770 void
771 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message)
772 {
773 	uid_t user = _GetUserID(message);
774 	if (user < 0)
775 		return;
776 
777 	if (user == 0 || fUserMode) {
778 		// Reset sticky events
779 		const char* name = message->GetString("name");
780 		const char* ownerName = message->GetString("owner");
781 		// TODO: support arguments (as selectors)
782 
783 		ExternalEventSource* event = _FindEvent(ownerName, name);
784 		if (event != NULL) {
785 			// Evaluate all of its jobs
786 			int32 count = event->CountListeners();
787 			for (int32 index = 0; index < count; index++) {
788 				BaseJob* listener = event->ListenerAt(index);
789 				Events::ResetStickyExternalEvent(listener->Event(), name);
790 			}
791 		}
792 	}
793 
794 	_ForwardEventMessage(user, message);
795 }
796 
797 
798 uid_t
799 LaunchDaemon::_GetUserID(BMessage* message)
800 {
801 	uid_t user = (uid_t)message->GetInt32("user", -1);
802 	if (user < 0) {
803 		BMessage reply((uint32)B_BAD_VALUE);
804 		message->SendReply(&reply);
805 	}
806 	return user;
807 }
808 
809 
810 void
811 LaunchDaemon::_ReadPaths(const BStringList& paths)
812 {
813 	for (int32 i = 0; i < paths.CountStrings(); i++) {
814 		BEntry entry(paths.StringAt(i));
815 		if (entry.InitCheck() != B_OK || !entry.Exists())
816 			continue;
817 
818 		_ReadDirectory(NULL, entry);
819 	}
820 }
821 
822 
823 void
824 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry)
825 {
826 	if (entry.IsDirectory())
827 		_ReadDirectory(context, entry);
828 	else
829 		_ReadFile(context, entry);
830 }
831 
832 
833 void
834 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry)
835 {
836 	BDirectory directory(&directoryEntry);
837 
838 	BEntry entry;
839 	while (directory.GetNextEntry(&entry) == B_OK) {
840 		_ReadEntry(context, entry);
841 	}
842 }
843 
844 
845 status_t
846 LaunchDaemon::_ReadFile(const char* context, BEntry& entry)
847 {
848 	BPath path;
849 	status_t status = path.SetTo(&entry);
850 	if (status != B_OK)
851 		return status;
852 
853 	SettingsParser parser;
854 	BMessage message;
855 	status = parser.ParseFile(path.Path(), message);
856 	if (status == B_OK) {
857 		_AddJobs(NULL, message);
858 		_AddTargets(message);
859 		_AddRunTargets(message);
860 	}
861 
862 	return status;
863 }
864 
865 
866 void
867 LaunchDaemon::_AddJobs(Target* target, BMessage& message)
868 {
869 	BMessage job;
870 	for (int32 index = 0; message.FindMessage("service", index,
871 			&job) == B_OK; index++) {
872 		_AddJob(target, true, job);
873 	}
874 
875 	for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK;
876 			index++) {
877 		_AddJob(target, false, job);
878 	}
879 }
880 
881 
882 void
883 LaunchDaemon::_AddTargets(BMessage& message)
884 {
885 	BMessage targetMessage;
886 	for (int32 index = 0; message.FindMessage("target", index,
887 			&targetMessage) == B_OK; index++) {
888 		const char* name = targetMessage.GetString("name");
889 		if (name == NULL) {
890 			// TODO: log error
891 			debug_printf("Target has no name, ignoring it!\n");
892 			continue;
893 		}
894 
895 		Target* target = FindTarget(name);
896 		if (target == NULL) {
897 			target = new Target(name);
898 			_AddTarget(target);
899 		} else if (targetMessage.GetBool("reset")) {
900 			// Remove all jobs from this target
901 			for (JobMap::iterator iterator = fJobs.begin();
902 					iterator != fJobs.end();) {
903 				Job* job = iterator->second;
904 				JobMap::iterator remove = iterator++;
905 
906 				if (job->Target() == target) {
907 					fJobs.erase(remove);
908 					delete job;
909 				}
910 			}
911 		}
912 
913 		_SetCondition(target, targetMessage);
914 		_SetEvent(target, targetMessage);
915 		_SetEnvironment(target, targetMessage);
916 		_AddJobs(target, targetMessage);
917 
918 		if (target->Event() != NULL)
919 			target->Event()->Register(*this);
920 	}
921 }
922 
923 
924 void
925 LaunchDaemon::_AddRunTargets(BMessage& message)
926 {
927 	BMessage runMessage;
928 	for (int32 index = 0; message.FindMessage("run", index,
929 			&runMessage) == B_OK; index++) {
930 		BMessage conditions;
931 		bool pass = true;
932 		if (runMessage.FindMessage("if", &conditions) == B_OK) {
933 			Condition* condition = Conditions::FromMessage(conditions);
934 			if (condition != NULL) {
935 				pass = condition->Test(*this);
936 				debug_printf("Test: %s -> %d\n", condition->ToString().String(),
937 					pass);
938 				delete condition;
939 			} else
940 				debug_printf("Could not parse condition!\n");
941 		}
942 
943 		if (pass) {
944 			_AddRunTargets(runMessage, NULL);
945 			_AddRunTargets(runMessage, "then");
946 		} else {
947 			_AddRunTargets(runMessage, "else");
948 		}
949 	}
950 }
951 
952 
953 void
954 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name)
955 {
956 	BMessage targets;
957 	if (name != NULL && message.FindMessage(name, &targets) != B_OK)
958 		return;
959 
960 	const char* target;
961 	for (int32 index = 0; targets.FindString("target", index, &target) == B_OK;
962 			index++) {
963 		fRunTargets.Add(target);
964 	}
965 }
966 
967 
968 void
969 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message)
970 {
971 	BString name = message.GetString("name");
972 	if (name.IsEmpty()) {
973 		// Invalid job description
974 		return;
975 	}
976 	name.ToLower();
977 
978 	Job* job = FindJob(name);
979 	if (job == NULL) {
980 		job = new (std::nothrow) Job(name);
981 		if (job == NULL)
982 			return;
983 
984 		job->SetService(service);
985 		job->SetCreateDefaultPort(service);
986 		job->SetTarget(target);
987 	}
988 
989 	if (message.HasBool("disabled"))
990 		job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled()));
991 
992 	if (message.HasBool("legacy"))
993 		job->SetCreateDefaultPort(!message.GetBool("legacy", !service));
994 
995 	_SetCondition(job, message);
996 	_SetEvent(job, message);
997 	_SetEnvironment(job, message);
998 
999 	BMessage portMessage;
1000 	for (int32 index = 0;
1001 			message.FindMessage("port", index, &portMessage) == B_OK; index++) {
1002 		job->AddPort(portMessage);
1003 	}
1004 
1005 	if (message.HasString("launch")) {
1006 		job->Arguments().MakeEmpty();
1007 
1008 		const char* argument;
1009 		for (int32 index = 0; message.FindString("launch", index, &argument)
1010 				== B_OK; index++) {
1011 			job->AddArgument(Utility::TranslatePath(argument));
1012 		}
1013 	}
1014 
1015 	const char* requirement;
1016 	for (int32 index = 0;
1017 			message.FindString("requires", index, &requirement) == B_OK;
1018 			index++) {
1019 		job->AddRequirement(requirement);
1020 	}
1021 	if (fInitTarget != NULL)
1022 		job->AddRequirement(fInitTarget->Name());
1023 
1024 	fJobs.insert(std::make_pair(job->Title(), job));
1025 }
1026 
1027 
1028 /*!	Initializes all jobs for the specified target (may be \c NULL).
1029 	Jobs that cannot be initialized, and those that never will be due to
1030 	conditions, will be removed from the list.
1031 */
1032 void
1033 LaunchDaemon::_InitJobs(Target* target)
1034 {
1035 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) {
1036 		Job* job = iterator->second;
1037 		JobMap::iterator remove = iterator++;
1038 
1039 		if (job->Target() != target)
1040 			continue;
1041 
1042 		status_t status = B_NO_INIT;
1043 		if (job->IsEnabled()) {
1044 			// Filter out jobs that have a constant and failing condition
1045 			if (job->Condition() == NULL || !job->Condition()->IsConstant(*this)
1046 				|| job->Condition()->Test(*this)) {
1047 				std::set<BString> dependencies;
1048 				status = job->Init(*this, dependencies);
1049 				if (status == B_OK && job->Event() != NULL)
1050 					status = job->Event()->Register(*this);
1051 			}
1052 		}
1053 
1054 		if (status != B_OK) {
1055 			if (status != B_NO_INIT) {
1056 				// TODO: log error
1057 				debug_printf("Init \"%s\" failed: %s\n", job->Name(),
1058 					strerror(status));
1059 			}
1060 
1061 			// Remove jobs that won't be used later on
1062 			fJobs.erase(remove);
1063 			delete job;
1064 		}
1065 	}
1066 }
1067 
1068 
1069 /*!	Adds all jobs for the specified target (may be \c NULL) to the launch
1070 	queue, except those that are triggered by events that haven't been
1071 	triggered yet.
1072 
1073 	Unless \a forceNow is true, the target is only launched if its events,
1074 	if any, have been triggered already, and its conditions are met.
1075 */
1076 void
1077 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow)
1078 {
1079 	if (!forceNow && target != NULL && (!target->EventHasTriggered()
1080 		|| !target->CheckCondition(*this))) {
1081 		return;
1082 	}
1083 
1084 	if (target != NULL && !target->HasLaunched()) {
1085 		target->SetLaunched(true);
1086 		_InitJobs(target);
1087 	}
1088 
1089 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1090 			iterator++) {
1091 		Job* job = iterator->second;
1092 		if (job->Target() == target)
1093 			_LaunchJob(job);
1094 	}
1095 }
1096 
1097 
1098 /*!	Adds the specified \a job to the launch queue
1099 	queue, except those that are triggered by events.
1100 
1101 	Unless \c FORCE_NOW is set, the target is only launched if its events,
1102 	if any, have been triggered already.
1103 
1104 	Calling this method will trigger a demand event if \c TRIGGER_DEMAND has
1105 	been set.
1106 */
1107 void
1108 LaunchDaemon::_LaunchJob(Job* job, uint32 options)
1109 {
1110 	if (job == NULL || job->IsLaunching() || job->IsRunning()
1111 		|| ((options & FORCE_NOW) == 0
1112 			&& (!job->EventHasTriggered() || !job->CheckCondition(*this)
1113 				|| ((options & TRIGGER_DEMAND) != 0
1114 						&& Events::TriggerDemand(job->Event()))))) {
1115 		return;
1116 	}
1117 
1118 	int32 count = job->Requirements().CountStrings();
1119 	for (int32 index = 0; index < count; index++) {
1120 		Job* requirement = FindJob(job->Requirements().StringAt(index));
1121 		if (requirement != NULL) {
1122 			// TODO: For jobs that have their communication channels set up,
1123 			// we would not need to trigger demand at this point
1124 			_LaunchJob(requirement, TRIGGER_DEMAND);
1125 		}
1126 	}
1127 
1128 	if (job->Target() != NULL)
1129 		job->Target()->ResolveSourceFiles();
1130 	if (job->Event() != NULL)
1131 		job->Event()->ResetTrigger();
1132 
1133 	job->SetLaunching(true);
1134 	fJobQueue.AddJob(job);
1135 }
1136 
1137 
1138 void
1139 LaunchDaemon::_AddTarget(Target* target)
1140 {
1141 	fTargets.insert(std::make_pair(target->Title(), target));
1142 }
1143 
1144 
1145 void
1146 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message)
1147 {
1148 	Condition* condition = job->Condition();
1149 	bool updated = false;
1150 
1151 	BMessage conditions;
1152 	if (message.FindMessage("if", &conditions) == B_OK) {
1153 		condition = Conditions::FromMessage(conditions);
1154 		updated = true;
1155 	}
1156 
1157 	if (message.GetBool("no_safemode")) {
1158 		condition = Conditions::AddNotSafeMode(condition);
1159 		updated = true;
1160 	}
1161 
1162 	if (updated)
1163 		job->SetCondition(condition);
1164 }
1165 
1166 
1167 void
1168 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message)
1169 {
1170 	Event* event = job->Event();
1171 	bool updated = false;
1172 
1173 	BMessage events;
1174 	if (message.FindMessage("on", &events) == B_OK) {
1175 		event = Events::FromMessage(this, events);
1176 		updated = true;
1177 	}
1178 
1179 	if (message.GetBool("on_demand")) {
1180 		event = Events::AddOnDemand(this, event);
1181 		updated = true;
1182 	}
1183 
1184 	if (updated) {
1185 		job->SetEvent(event);
1186 		_ResolveExternalEvents(job);
1187 	}
1188 }
1189 
1190 
1191 void
1192 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message)
1193 {
1194 	BMessage environmentMessage;
1195 	if (message.FindMessage("env", &environmentMessage) == B_OK)
1196 		job->SetEnvironment(environmentMessage);
1197 }
1198 
1199 
1200 ExternalEventSource*
1201 LaunchDaemon::_FindEvent(const char* owner, const char* name) const
1202 {
1203 	if (name == NULL)
1204 		return NULL;
1205 
1206 	BString eventName = name;
1207 	eventName.ToLower();
1208 
1209 	EventMap::const_iterator found = fEvents.find(eventName);
1210 	if (found != fEvents.end())
1211 		return found->second;
1212 
1213 	if (owner == NULL)
1214 		return NULL;
1215 
1216 	eventName.Prepend("/");
1217 	eventName.Prepend(get_leaf(owner));
1218 
1219 	found = fEvents.find(eventName);
1220 	if (found != fEvents.end())
1221 		return found->second;
1222 
1223 	return NULL;
1224 }
1225 
1226 
1227 void
1228 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event,
1229 	const BString& name)
1230 {
1231 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1232 			iterator++) {
1233 		Job* job = iterator->second;
1234 		if (Events::ResolveExternalEvent(job->Event(), name, event->Flags()))
1235 			event->AddListener(job);
1236 	}
1237 }
1238 
1239 
1240 void
1241 LaunchDaemon::_ResolveExternalEvents(BaseJob* job)
1242 {
1243 	if (job->Event() == NULL)
1244 		return;
1245 
1246 	for (EventMap::iterator iterator = fEvents.begin();
1247 			iterator != fEvents.end(); iterator++) {
1248 		ExternalEventSource* event = iterator->second;
1249 		if (Events::ResolveExternalEvent(job->Event(), event->Name(),
1250 				event->Flags()))
1251 			event->AddListener(job);
1252 	}
1253 }
1254 
1255 
1256 void
1257 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message)
1258 {
1259 	if (fUserMode)
1260 		return;
1261 
1262 	// Forward event to user launch_daemon(s)
1263 	if (user == 0) {
1264 		for (SessionMap::iterator iterator = fSessions.begin();
1265 				iterator != fSessions.end(); iterator++) {
1266 			Session* session = iterator->second;
1267 			session->Daemon().SendMessage(message);
1268 				// ignore reply
1269 		}
1270 	} else {
1271 		Session* session = FindSession(user);
1272 		if (session != NULL)
1273 			session->Daemon().SendMessage(message);
1274 	}
1275 }
1276 
1277 
1278 status_t
1279 LaunchDaemon::_StartSession(const char* login)
1280 {
1281 	// TODO: enable user/group code
1282 	// The launch_daemon currently cannot talk to the registrar, though
1283 
1284 	struct passwd* passwd = getpwnam(login);
1285 	if (passwd == NULL)
1286 		return B_NAME_NOT_FOUND;
1287 	if (strcmp(passwd->pw_name, login) != 0)
1288 		return B_NAME_NOT_FOUND;
1289 
1290 	// Check if there is a user session running already
1291 	uid_t user = passwd->pw_uid;
1292 	gid_t group = passwd->pw_gid;
1293 
1294 	Unlock();
1295 
1296 	if (fork() == 0) {
1297 		if (setsid() < 0)
1298 			exit(EXIT_FAILURE);
1299 
1300 		if (initgroups(login, group) == -1)
1301 			exit(EXIT_FAILURE);
1302 		if (setgid(group) != 0)
1303 			exit(EXIT_FAILURE);
1304 		if (setuid(user) != 0)
1305 			exit(EXIT_FAILURE);
1306 
1307 		if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') {
1308 			setenv("HOME", passwd->pw_dir, true);
1309 
1310 			if (chdir(passwd->pw_dir) != 0) {
1311 				debug_printf("Could not switch to home dir %s: %s\n",
1312 					passwd->pw_dir, strerror(errno));
1313 			}
1314 		}
1315 
1316 		// TODO: This leaks the parent application
1317 		be_app = NULL;
1318 
1319 		// TODO: take over system jobs, and reserve their names
1320 		status_t status;
1321 		LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status);
1322 		if (status == B_OK)
1323 			daemon->Run();
1324 
1325 		delete daemon;
1326 		exit(EXIT_SUCCESS);
1327 	}
1328 	Lock();
1329 	return B_OK;
1330 }
1331 
1332 
1333 void
1334 LaunchDaemon::_RetrieveKernelOptions()
1335 {
1336 	char buffer[32];
1337 	size_t size = sizeof(buffer);
1338 	status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer,
1339 		&size);
1340 	if (status == B_OK) {
1341 		fSafeMode = !strncasecmp(buffer, "true", size)
1342 			|| !strncasecmp(buffer, "yes", size)
1343 			|| !strncasecmp(buffer, "on", size)
1344 			|| !strncasecmp(buffer, "enabled", size);
1345 	} else
1346 		fSafeMode = false;
1347 }
1348 
1349 
1350 void
1351 LaunchDaemon::_SetupEnvironment()
1352 {
1353 	// Determine safemode kernel option
1354 	setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true);
1355 
1356 	// Default locale settings
1357 	setenv("LC_TYPE", "en_US.UTF-8", true);
1358 }
1359 
1360 
1361 /*!	Basic system initialization that must happen before any jobs are launched.
1362 */
1363 void
1364 LaunchDaemon::_InitSystem()
1365 {
1366 	_AddInitJob(new InitRealTimeClockJob());
1367 	_AddInitJob(new InitSharedMemoryDirectoryJob());
1368 	_AddInitJob(new InitTemporaryDirectoryJob());
1369 
1370 	fJobQueue.AddJob(fInitTarget);
1371 }
1372 
1373 
1374 void
1375 LaunchDaemon::_AddInitJob(BJob* job)
1376 {
1377 	fInitTarget->AddDependency(job);
1378 	fJobQueue.AddJob(job);
1379 }
1380 
1381 
1382 // #pragma mark -
1383 
1384 
1385 static void
1386 open_stdio(int targetFD, int openMode)
1387 {
1388 	int fd = open("/dev/null", openMode);
1389 	if (fd != targetFD) {
1390 		dup2(fd, targetFD);
1391 		close(fd);
1392 	}
1393 }
1394 
1395 
1396 int
1397 main()
1398 {
1399 	// Make stdin/out/err available
1400 	open_stdio(STDIN_FILENO, O_RDONLY);
1401 	open_stdio(STDOUT_FILENO, O_WRONLY);
1402 	dup2(STDOUT_FILENO, STDERR_FILENO);
1403 
1404 	EventMap events;
1405 	status_t status;
1406 	LaunchDaemon* daemon = new LaunchDaemon(false, events, status);
1407 	if (status == B_OK)
1408 		daemon->Run();
1409 
1410 	delete daemon;
1411 	return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;
1412 }
1413