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