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