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