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