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