xref: /haiku/src/servers/launch/LaunchDaemon.cpp (revision 427edfcf0ddc74fc461c9355484d33fd4b027b70)
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 					// TODO: take restart throttle into account
531 					// TODO: don't restart on shutdown
532 					_LaunchJob(job);
533 				}
534 			}
535 			break;
536 		}
537 		case B_SOME_APP_LAUNCHED:
538 		{
539 			team_id team = (team_id)message->GetInt32("be:team", -1);
540 			Job* job = NULL;
541 
542 			MutexLocker locker(fTeamsLock);
543 
544 			TeamMap::iterator found = fTeams.find(team);
545 			if (found != fTeams.end()) {
546 				job = found->second;
547 				locker.Unlock();
548 			} else {
549 				locker.Unlock();
550 
551 				// Find job by name instead
552 				const char* signature = message->GetString("be:signature");
553 				job = FindJob(get_leaf(signature));
554 				if (job != NULL) {
555 					TRACE("Updated default port of untracked team %d, %s\n",
556 						(int)team, signature);
557 				}
558 			}
559 
560 			if (job != NULL) {
561 				// Update port info
562 				app_info info;
563 				status_t status = be_roster->GetRunningAppInfo(team, &info);
564 				if (status == B_OK && info.port != job->DefaultPort()) {
565 					TRACE("Update default port for %s to %d\n", job->Name(),
566 						(int)info.port);
567 					job->SetDefaultPort(info.port);
568 				}
569 			}
570 			break;
571 		}
572 
573 		case B_GET_LAUNCH_DATA:
574 			_HandleGetLaunchData(message);
575 			break;
576 
577 		case B_LAUNCH_TARGET:
578 			_HandleLaunchTarget(message);
579 			break;
580 		case B_STOP_LAUNCH_TARGET:
581 			_HandleStopLaunchTarget(message);
582 			break;
583 		case B_LAUNCH_JOB:
584 			_HandleLaunchJob(message);
585 			break;
586 		case B_ENABLE_LAUNCH_JOB:
587 			_HandleEnableLaunchJob(message);
588 			break;
589 		case B_STOP_LAUNCH_JOB:
590 			_HandleStopLaunchJob(message);
591 			break;
592 
593 		case B_LAUNCH_SESSION:
594 			_HandleLaunchSession(message);
595 			break;
596 		case B_REGISTER_SESSION_DAEMON:
597 			_HandleRegisterSessionDaemon(message);
598 			break;
599 
600 		case B_REGISTER_LAUNCH_EVENT:
601 			_HandleRegisterLaunchEvent(message);
602 			break;
603 		case B_UNREGISTER_LAUNCH_EVENT:
604 			_HandleUnregisterLaunchEvent(message);
605 			break;
606 		case B_NOTIFY_LAUNCH_EVENT:
607 			_HandleNotifyLaunchEvent(message);
608 			break;
609 		case B_RESET_STICKY_LAUNCH_EVENT:
610 			_HandleResetStickyLaunchEvent(message);
611 			break;
612 
613 		case B_GET_LAUNCH_TARGETS:
614 			_HandleGetLaunchTargets(message);
615 			break;
616 		case B_GET_LAUNCH_TARGET_INFO:
617 			_HandleGetLaunchTargetInfo(message);
618 			break;
619 		case B_GET_LAUNCH_JOBS:
620 			_HandleGetLaunchJobs(message);
621 			break;
622 		case B_GET_LAUNCH_JOB_INFO:
623 			_HandleGetLaunchJobInfo(message);
624 			break;
625 
626 		case B_GET_LAUNCH_LOG:
627 			_HandleGetLaunchLog(message);
628 			break;
629 
630 		case kMsgEventTriggered:
631 		{
632 			// An internal event has been triggered.
633 			// Check if its job(s) can be launched now.
634 			const char* name = message->GetString("owner");
635 			if (name == NULL)
636 				break;
637 
638 			Event* event = (Event*)message->GetPointer("event");
639 
640 			Job* job = FindJob(name);
641 			if (job != NULL) {
642 				fLog.EventTriggered(job, event);
643 				_LaunchJob(job);
644 				break;
645 			}
646 
647 			Target* target = FindTarget(name);
648 			if (target != NULL) {
649 				fLog.EventTriggered(target, event);
650 				_LaunchJobs(target);
651 				break;
652 			}
653 			break;
654 		}
655 
656 		default:
657 			BServer::MessageReceived(message);
658 			break;
659 	}
660 }
661 
662 
663 void
664 LaunchDaemon::_HandleGetLaunchData(BMessage* message)
665 {
666 	uid_t user = _GetUserID(message);
667 	if (user < 0)
668 		return;
669 
670 	BMessage reply((uint32)B_OK);
671 	bool launchJob = true;
672 
673 	Job* job = FindJob(get_leaf(message->GetString("name")));
674 	if (job == NULL) {
675 		Session* session = FindSession(user);
676 		if (session != NULL) {
677 			// Forward request to user launch_daemon
678 			if (session->Daemon().SendMessage(message) == B_OK)
679 				return;
680 		}
681 		reply.what = B_NAME_NOT_FOUND;
682 	} else if (job->IsService() && !job->IsLaunched()) {
683 		if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) {
684 			// The job exists, but cannot be started yet, as its
685 			// conditions are not met; don't make it available yet
686 			// TODO: we may not want to initialize jobs with conditions
687 			// that aren't met yet
688 			reply.what = B_NO_INIT;
689 		} else if (job->Event() != NULL) {
690 			if (!Events::TriggerDemand(job->Event())) {
691 				// The job is not triggered by demand; we cannot start it now
692 				reply.what = B_NO_INIT;
693 			} else {
694 				// The job has already been triggered, don't launch it again
695 				launchJob = false;
696 			}
697 		}
698 	} else
699 		launchJob = false;
700 
701 	bool ownsMessage = false;
702 	if (reply.what == B_OK) {
703 		// Launch the job if it hasn't been launched already
704 		if (launchJob)
705 			_LaunchJob(job, TRIGGER_DEMAND);
706 
707 		DetachCurrentMessage();
708 		status_t result = job->HandleGetLaunchData(message);
709 		if (result == B_OK) {
710 			// Replying is delegated to the job.
711 			return;
712 		}
713 
714 		ownsMessage = true;
715 		reply.what = result;
716 	}
717 
718 	message->SendReply(&reply);
719 	if (ownsMessage)
720 		delete message;
721 }
722 
723 
724 void
725 LaunchDaemon::_HandleLaunchTarget(BMessage* message)
726 {
727 	uid_t user = _GetUserID(message);
728 	if (user < 0)
729 		return;
730 
731 	const char* name = message->GetString("target");
732 	const char* baseName = message->GetString("base target");
733 
734 	Target* target = FindTarget(name);
735 	if (target == NULL && baseName != NULL) {
736 		Target* baseTarget = FindTarget(baseName);
737 		if (baseTarget != NULL) {
738 			target = new Target(name);
739 
740 			// Copy all jobs with the base target into the new target
741 			for (JobMap::iterator iterator = fJobs.begin();
742 					iterator != fJobs.end();) {
743 				Job* job = iterator->second;
744 				iterator++;
745 
746 				if (job->Target() == baseTarget) {
747 					Job* copy = new Job(*job);
748 					copy->SetTarget(target);
749 
750 					fJobs.insert(std::make_pair(copy->Name(), copy));
751 				}
752 			}
753 		}
754 	}
755 	if (target == NULL) {
756 		Session* session = FindSession(user);
757 		if (session != NULL) {
758 			// Forward request to user launch_daemon
759 			if (session->Daemon().SendMessage(message) == B_OK)
760 				return;
761 		}
762 
763 		BMessage reply(B_NAME_NOT_FOUND);
764 		message->SendReply(&reply);
765 		return;
766 	}
767 
768 	BMessage data;
769 	if (message->FindMessage("data", &data) == B_OK)
770 		target->AddData(data.GetString("name"), data);
771 
772 	_LaunchJobs(target);
773 
774 	BMessage reply((uint32)B_OK);
775 	message->SendReply(&reply);
776 }
777 
778 
779 void
780 LaunchDaemon::_HandleStopLaunchTarget(BMessage* message)
781 {
782 	uid_t user = _GetUserID(message);
783 	if (user < 0)
784 		return;
785 
786 	const char* name = message->GetString("target");
787 
788 	Target* target = FindTarget(name);
789 	if (target == NULL) {
790 		Session* session = FindSession(user);
791 		if (session != NULL) {
792 			// Forward request to user launch_daemon
793 			if (session->Daemon().SendMessage(message) == B_OK)
794 				return;
795 		}
796 
797 		BMessage reply(B_NAME_NOT_FOUND);
798 		message->SendReply(&reply);
799 		return;
800 	}
801 
802 	BMessage data;
803 	if (message->FindMessage("data", &data) == B_OK)
804 		target->AddData(data.GetString("name"), data);
805 
806 	bool force = message->GetBool("force");
807 	fLog.JobStopped(target, force);
808 	_StopJobs(target, force);
809 
810 	BMessage reply((uint32)B_OK);
811 	message->SendReply(&reply);
812 }
813 
814 
815 void
816 LaunchDaemon::_HandleLaunchJob(BMessage* message)
817 {
818 	uid_t user = _GetUserID(message);
819 	if (user < 0)
820 		return;
821 
822 	const char* name = message->GetString("name");
823 
824 	Job* job = FindJob(name);
825 	if (job == NULL) {
826 		Session* session = FindSession(user);
827 		if (session != NULL) {
828 			// Forward request to user launch_daemon
829 			if (session->Daemon().SendMessage(message) == B_OK)
830 				return;
831 		}
832 
833 		BMessage reply(B_NAME_NOT_FOUND);
834 		message->SendReply(&reply);
835 		return;
836 	}
837 
838 	job->SetEnabled(true);
839 	_LaunchJob(job, FORCE_NOW);
840 
841 	BMessage reply((uint32)B_OK);
842 	message->SendReply(&reply);
843 }
844 
845 
846 void
847 LaunchDaemon::_HandleEnableLaunchJob(BMessage* message)
848 {
849 	uid_t user = _GetUserID(message);
850 	if (user < 0)
851 		return;
852 
853 	const char* name = message->GetString("name");
854 	bool enable = message->GetBool("enable");
855 
856 	Job* job = FindJob(name);
857 	if (job == NULL) {
858 		Session* session = FindSession(user);
859 		if (session != NULL) {
860 			// Forward request to user launch_daemon
861 			if (session->Daemon().SendMessage(message) == B_OK)
862 				return;
863 		}
864 
865 		BMessage reply(B_NAME_NOT_FOUND);
866 		message->SendReply(&reply);
867 		return;
868 	}
869 
870 	job->SetEnabled(enable);
871 	fLog.JobEnabled(job, enable);
872 
873 	BMessage reply((uint32)B_OK);
874 	message->SendReply(&reply);
875 }
876 
877 
878 void
879 LaunchDaemon::_HandleStopLaunchJob(BMessage* message)
880 {
881 	uid_t user = _GetUserID(message);
882 	if (user < 0)
883 		return;
884 
885 	const char* name = message->GetString("name");
886 
887 	Job* job = FindJob(name);
888 	if (job == NULL) {
889 		Session* session = FindSession(user);
890 		if (session != NULL) {
891 			// Forward request to user launch_daemon
892 			if (session->Daemon().SendMessage(message) == B_OK)
893 				return;
894 		}
895 
896 		BMessage reply(B_NAME_NOT_FOUND);
897 		message->SendReply(&reply);
898 		return;
899 	}
900 
901 	bool force = message->GetBool("force");
902 	fLog.JobStopped(job, force);
903 	_StopJob(job, force);
904 
905 	BMessage reply((uint32)B_OK);
906 	message->SendReply(&reply);
907 }
908 
909 
910 void
911 LaunchDaemon::_HandleLaunchSession(BMessage* message)
912 {
913 	uid_t user = _GetUserID(message);
914 	if (user < 0)
915 		return;
916 
917 	status_t status = B_OK;
918 	const char* login = message->GetString("login");
919 	if (login == NULL)
920 		status = B_BAD_VALUE;
921 	if (status == B_OK && user != 0) {
922 		// Only the root user can start sessions
923 		// TODO: we'd actually need to know the uid of the sender
924 		status = B_PERMISSION_DENIED;
925 	}
926 	if (status == B_OK)
927 		status = _StartSession(login);
928 
929 	BMessage reply((uint32)status);
930 	message->SendReply(&reply);
931 }
932 
933 
934 void
935 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message)
936 {
937 	uid_t user = _GetUserID(message);
938 	if (user < 0)
939 		return;
940 
941 	status_t status = B_OK;
942 
943 	BMessenger target;
944 	if (message->FindMessenger("daemon", &target) != B_OK)
945 		status = B_BAD_VALUE;
946 
947 	if (status == B_OK) {
948 		Session* session = new (std::nothrow) Session(user, target);
949 		if (session != NULL)
950 			fSessions.insert(std::make_pair(user, session));
951 		else
952 			status = B_NO_MEMORY;
953 	}
954 
955 	BMessage reply((uint32)status);
956 	message->SendReply(&reply);
957 }
958 
959 
960 void
961 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
962 {
963 	uid_t user = _GetUserID(message);
964 	if (user < 0)
965 		return;
966 
967 	if (user == 0 || fUserMode) {
968 		status_t status = B_OK;
969 
970 		const char* name = message->GetString("name");
971 		const char* ownerName = message->GetString("owner");
972 		uint32 flags = message->GetUInt32("flags", 0);
973 		BMessenger source;
974 		if (name != NULL && ownerName != NULL
975 			&& message->FindMessenger("source", &source) == B_OK) {
976 			// Register event
977 			ownerName = get_leaf(ownerName);
978 
979 			ExternalEventSource* event = new (std::nothrow)
980 				ExternalEventSource(source, ownerName, name, flags);
981 			if (event != NULL) {
982 				// Use short name, and fully qualified name
983 				BString eventName = name;
984 				fEvents.insert(std::make_pair(eventName, event));
985 				_ResolveExternalEvents(event, eventName);
986 
987 				eventName.Prepend("/");
988 				eventName.Prepend(ownerName);
989 				fEvents.insert(std::make_pair(eventName, event));
990 				_ResolveExternalEvents(event, eventName);
991 
992 				fLog.ExternalEventRegistered(name);
993 			} else
994 				status = B_NO_MEMORY;
995 		} else
996 			status = B_BAD_VALUE;
997 
998 		BMessage reply((uint32)status);
999 		message->SendReply(&reply);
1000 	}
1001 
1002 	_ForwardEventMessage(user, message);
1003 }
1004 
1005 
1006 void
1007 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message)
1008 {
1009 	uid_t user = _GetUserID(message);
1010 	if (user < 0)
1011 		return;
1012 
1013 	if (user == 0 || fUserMode) {
1014 		status_t status = B_OK;
1015 
1016 		const char* name = message->GetString("name");
1017 		const char* ownerName = message->GetString("owner");
1018 		BMessenger source;
1019 		if (name != NULL && ownerName != NULL
1020 			&& message->FindMessenger("source", &source) == B_OK) {
1021 			// Unregister short and fully qualified event name
1022 			ownerName = get_leaf(ownerName);
1023 
1024 			BString eventName = name;
1025 			fEvents.erase(eventName);
1026 
1027 			eventName.Prepend("/");
1028 			eventName.Prepend(ownerName);
1029 			fEvents.erase(eventName);
1030 
1031 			fLog.ExternalEventRegistered(name);
1032 		} else
1033 			status = B_BAD_VALUE;
1034 
1035 		BMessage reply((uint32)status);
1036 		message->SendReply(&reply);
1037 	}
1038 
1039 	_ForwardEventMessage(user, message);
1040 }
1041 
1042 
1043 void
1044 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message)
1045 {
1046 	uid_t user = _GetUserID(message);
1047 	if (user < 0)
1048 		return;
1049 
1050 	if (user == 0 || fUserMode) {
1051 		// Trigger events
1052 		const char* name = message->GetString("name");
1053 		const char* ownerName = message->GetString("owner");
1054 		// TODO: support arguments (as selectors)
1055 
1056 		ExternalEventSource* event = _FindEvent(ownerName, name);
1057 		if (event != NULL) {
1058 			fLog.ExternalEventTriggered(name);
1059 
1060 			// Evaluate all of its jobs
1061 			int32 count = event->CountListeners();
1062 			for (int32 index = 0; index < count; index++) {
1063 				BaseJob* listener = event->ListenerAt(index);
1064 				Events::TriggerExternalEvent(listener->Event(), name);
1065 			}
1066 		}
1067 	}
1068 
1069 	_ForwardEventMessage(user, message);
1070 }
1071 
1072 
1073 void
1074 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message)
1075 {
1076 	uid_t user = _GetUserID(message);
1077 	if (user < 0)
1078 		return;
1079 
1080 	if (user == 0 || fUserMode) {
1081 		// Reset sticky events
1082 		const char* name = message->GetString("name");
1083 		const char* ownerName = message->GetString("owner");
1084 		// TODO: support arguments (as selectors)
1085 
1086 		ExternalEventSource* event = _FindEvent(ownerName, name);
1087 		if (event != NULL) {
1088 			// Evaluate all of its jobs
1089 			int32 count = event->CountListeners();
1090 			for (int32 index = 0; index < count; index++) {
1091 				BaseJob* listener = event->ListenerAt(index);
1092 				Events::ResetStickyExternalEvent(listener->Event(), name);
1093 			}
1094 		}
1095 	}
1096 
1097 	_ForwardEventMessage(user, message);
1098 }
1099 
1100 
1101 void
1102 LaunchDaemon::_HandleGetLaunchTargets(BMessage* message)
1103 {
1104 	uid_t user = _GetUserID(message);
1105 	if (user < 0)
1106 		return;
1107 
1108 	BMessage reply;
1109 	status_t status = B_OK;
1110 
1111 	if (!fUserMode) {
1112 		// Request the data from the user's daemon, too
1113 		Session* session = FindSession(user);
1114 		if (session != NULL) {
1115 			BMessage request(B_GET_LAUNCH_TARGETS);
1116 			status = request.AddInt32("user", 0);
1117 			if (status == B_OK) {
1118 				status = session->Daemon().SendMessage(&request,
1119 					&reply);
1120 			}
1121 			if (status == B_OK)
1122 				status = reply.what;
1123 		} else
1124 			status = B_NAME_NOT_FOUND;
1125 	}
1126 
1127 	if (status == B_OK) {
1128 		TargetMap::const_iterator iterator = fTargets.begin();
1129 		for (; iterator != fTargets.end(); iterator++)
1130 			reply.AddString("target", iterator->first);
1131 	}
1132 
1133 	reply.what = status;
1134 	message->SendReply(&reply);
1135 }
1136 
1137 
1138 void
1139 LaunchDaemon::_HandleGetLaunchTargetInfo(BMessage* message)
1140 {
1141 	uid_t user = _GetUserID(message);
1142 	if (user < 0)
1143 		return;
1144 
1145 	const char* name = message->GetString("name");
1146 	Target* target = FindTarget(name);
1147 	if (target == NULL && !fUserMode) {
1148 		_ForwardEventMessage(user, message);
1149 		return;
1150 	}
1151 
1152 	BMessage info(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND));
1153 	if (target != NULL) {
1154 		_GetBaseJobInfo(target, info);
1155 
1156 		for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1157 				iterator++) {
1158 			Job* job = iterator->second;
1159 			if (job->Target() == target)
1160 				info.AddString("job", job->Name());
1161 		}
1162 	}
1163 	message->SendReply(&info);
1164 }
1165 
1166 
1167 void
1168 LaunchDaemon::_HandleGetLaunchJobs(BMessage* message)
1169 {
1170 	uid_t user = _GetUserID(message);
1171 	if (user < 0)
1172 		return;
1173 
1174 	const char* targetName = message->GetString("target");
1175 
1176 	BMessage reply;
1177 	status_t status = B_OK;
1178 
1179 	if (!fUserMode) {
1180 		// Request the data from the user's daemon, too
1181 		Session* session = FindSession(user);
1182 		if (session != NULL) {
1183 			BMessage request(B_GET_LAUNCH_JOBS);
1184 			status = request.AddInt32("user", 0);
1185 			if (status == B_OK && targetName != NULL)
1186 				status = request.AddString("target", targetName);
1187 			if (status == B_OK) {
1188 				status = session->Daemon().SendMessage(&request,
1189 					&reply);
1190 			}
1191 			if (status == B_OK)
1192 				status = reply.what;
1193 		} else
1194 			status = B_NAME_NOT_FOUND;
1195 	}
1196 
1197 	if (status == B_OK) {
1198 		JobMap::const_iterator iterator = fJobs.begin();
1199 		for (; iterator != fJobs.end(); iterator++) {
1200 			Job* job = iterator->second;
1201 			if (targetName != NULL && (job->Target() == NULL
1202 					|| job->Target()->Title() != targetName)) {
1203 				continue;
1204 			}
1205 			reply.AddString("job", iterator->first);
1206 		}
1207 	}
1208 
1209 	reply.what = status;
1210 	message->SendReply(&reply);
1211 }
1212 
1213 
1214 void
1215 LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message)
1216 {
1217 	uid_t user = _GetUserID(message);
1218 	if (user < 0)
1219 		return;
1220 
1221 	const char* name = message->GetString("name");
1222 	Job* job = FindJob(name);
1223 	if (job == NULL && !fUserMode) {
1224 		_ForwardEventMessage(user, message);
1225 		return;
1226 	}
1227 
1228 	BMessage info(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND));
1229 	if (job != NULL) {
1230 		_GetBaseJobInfo(job, info);
1231 
1232 		info.SetInt32("team", job->Team());
1233 		info.SetBool("enabled", job->IsEnabled());
1234 		info.SetBool("running", job->IsRunning());
1235 		info.SetBool("launched", job->IsLaunched());
1236 		info.SetBool("service", job->IsService());
1237 
1238 		if (job->Target() != NULL)
1239 			info.SetString("target", job->Target()->Name());
1240 
1241 		for (int32 i = 0; i < job->Arguments().CountStrings(); i++)
1242 			info.AddString("launch", job->Arguments().StringAt(i));
1243 
1244 		for (int32 i = 0; i < job->Requirements().CountStrings(); i++)
1245 			info.AddString("requires", job->Requirements().StringAt(i));
1246 
1247 		PortMap::const_iterator iterator = job->Ports().begin();
1248 		for (; iterator != job->Ports().end(); iterator++)
1249 			info.AddMessage("port", &iterator->second);
1250 	}
1251 	message->SendReply(&info);
1252 }
1253 
1254 
1255 void
1256 LaunchDaemon::_HandleGetLaunchLog(BMessage* message)
1257 {
1258 	uid_t user = _GetUserID(message);
1259 	if (user < 0)
1260 		return;
1261 
1262 	BMessage filter;
1263 	BString jobName;
1264 	const char* event = NULL;
1265 	int32 limit = 0;
1266 	bool systemOnly = false;
1267 	bool userOnly = false;
1268 	if (message->FindMessage("filter", &filter) == B_OK) {
1269 		limit = filter.GetInt32("limit", 0);
1270 		jobName = filter.GetString("job");
1271 		jobName.ToLower();
1272 		event = filter.GetString("event");
1273 		systemOnly = filter.GetBool("systemOnly");
1274 		userOnly = filter.GetBool("userOnly");
1275 	}
1276 
1277 	BMessage info((uint32)B_OK);
1278 	int32 count = 0;
1279 
1280 	if (user == 0 || !userOnly) {
1281 		LogItemList::Iterator iterator = fLog.Iterator();
1282 		while (iterator.HasNext()) {
1283 			LogItem* item = iterator.Next();
1284 			if (!item->Matches(jobName.IsEmpty() ? NULL : jobName.String(),
1285 					event)) {
1286 				continue;
1287 			}
1288 
1289 			BMessage itemMessage;
1290 			itemMessage.AddUInt64("when", item->When());
1291 			itemMessage.AddInt32("type", (int32)item->Type());
1292 			itemMessage.AddString("message", item->Message());
1293 
1294 			BMessage parameter;
1295 			item->GetParameter(parameter);
1296 			itemMessage.AddMessage("parameter", &parameter);
1297 
1298 			info.AddMessage("item", &itemMessage);
1299 
1300 			// limit == 0 means no limit
1301 			if (++count == limit)
1302 				break;
1303 		}
1304 	}
1305 
1306 	// Get the list from the user daemon, and merge it into our reply
1307 	Session* session = FindSession(user);
1308 	if (session != NULL && !systemOnly) {
1309 		if (limit != 0) {
1310 			// Update limit for user daemon
1311 			limit -= count;
1312 			if (limit <= 0) {
1313 				message->SendReply(&info);
1314 				return;
1315 			}
1316 		}
1317 
1318 		BMessage reply;
1319 
1320 		BMessage request(B_GET_LAUNCH_LOG);
1321 		status_t status = request.AddInt32("user", 0);
1322 		if (status == B_OK && (limit != 0 || !jobName.IsEmpty()
1323 				|| event != NULL)) {
1324 			// Forward filter specification when needed
1325 			status = filter.SetInt32("limit", limit);
1326 			if (status == B_OK)
1327 				status = request.AddMessage("filter", &filter);
1328 		}
1329 		if (status == B_OK)
1330 			status = session->Daemon().SendMessage(&request, &reply);
1331 		if (status == B_OK)
1332 			info.AddMessage("user", &reply);
1333 	}
1334 
1335 	message->SendReply(&info);
1336 }
1337 
1338 
1339 uid_t
1340 LaunchDaemon::_GetUserID(BMessage* message)
1341 {
1342 	uid_t user = (uid_t)message->GetInt32("user", -1);
1343 	if (user < 0) {
1344 		BMessage reply((uint32)B_BAD_VALUE);
1345 		message->SendReply(&reply);
1346 	}
1347 	return user;
1348 }
1349 
1350 
1351 void
1352 LaunchDaemon::_ReadPaths(const BStringList& paths)
1353 {
1354 	for (int32 i = 0; i < paths.CountStrings(); i++) {
1355 		BEntry entry(paths.StringAt(i));
1356 		if (entry.InitCheck() != B_OK || !entry.Exists())
1357 			continue;
1358 
1359 		_ReadDirectory(NULL, entry);
1360 	}
1361 }
1362 
1363 
1364 void
1365 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry)
1366 {
1367 	if (entry.IsDirectory())
1368 		_ReadDirectory(context, entry);
1369 	else
1370 		_ReadFile(context, entry);
1371 }
1372 
1373 
1374 void
1375 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry)
1376 {
1377 	BDirectory directory(&directoryEntry);
1378 
1379 	BEntry entry;
1380 	while (directory.GetNextEntry(&entry) == B_OK) {
1381 		_ReadEntry(context, entry);
1382 	}
1383 }
1384 
1385 
1386 status_t
1387 LaunchDaemon::_ReadFile(const char* context, BEntry& entry)
1388 {
1389 	BPath path;
1390 	status_t status = path.SetTo(&entry);
1391 	if (status != B_OK)
1392 		return status;
1393 
1394 	SettingsParser parser;
1395 	BMessage message;
1396 	status = parser.ParseFile(path.Path(), message);
1397 	if (status == B_OK) {
1398 		TRACE("launch_daemon: read file %s\n", path.Path());
1399 		_AddJobs(NULL, message);
1400 		_AddTargets(message);
1401 		_AddRunTargets(message);
1402 	}
1403 
1404 	return status;
1405 }
1406 
1407 
1408 void
1409 LaunchDaemon::_AddJobs(Target* target, BMessage& message)
1410 {
1411 	BMessage job;
1412 	for (int32 index = 0; message.FindMessage("service", index,
1413 			&job) == B_OK; index++) {
1414 		_AddJob(target, true, job);
1415 	}
1416 
1417 	for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK;
1418 			index++) {
1419 		_AddJob(target, false, job);
1420 	}
1421 }
1422 
1423 
1424 void
1425 LaunchDaemon::_AddTargets(BMessage& message)
1426 {
1427 	BMessage targetMessage;
1428 	for (int32 index = 0; message.FindMessage("target", index,
1429 			&targetMessage) == B_OK; index++) {
1430 		const char* name = targetMessage.GetString("name");
1431 		if (name == NULL) {
1432 			// TODO: log error
1433 			debug_printf("Target has no name, ignoring it!\n");
1434 			continue;
1435 		}
1436 
1437 		Target* target = FindTarget(name);
1438 		if (target == NULL) {
1439 			target = new Target(name);
1440 			_AddTarget(target);
1441 		} else if (targetMessage.GetBool("reset")) {
1442 			// Remove all jobs from this target
1443 			for (JobMap::iterator iterator = fJobs.begin();
1444 					iterator != fJobs.end();) {
1445 				Job* job = iterator->second;
1446 				JobMap::iterator remove = iterator++;
1447 
1448 				if (job->Target() == target) {
1449 					fJobs.erase(remove);
1450 					delete job;
1451 				}
1452 			}
1453 		}
1454 
1455 		_SetCondition(target, targetMessage);
1456 		_SetEvent(target, targetMessage);
1457 		_SetEnvironment(target, targetMessage);
1458 		_AddJobs(target, targetMessage);
1459 
1460 		if (target->Event() != NULL)
1461 			target->Event()->Register(*this);
1462 	}
1463 }
1464 
1465 
1466 void
1467 LaunchDaemon::_AddRunTargets(BMessage& message)
1468 {
1469 	BMessage runMessage;
1470 	for (int32 index = 0; message.FindMessage("run", index,
1471 			&runMessage) == B_OK; index++) {
1472 		BMessage conditions;
1473 		bool pass = true;
1474 		if (runMessage.FindMessage("if", &conditions) == B_OK) {
1475 			Condition* condition = Conditions::FromMessage(conditions);
1476 			if (condition != NULL) {
1477 				pass = condition->Test(*this);
1478 				debug_printf("Test: %s -> %d\n", condition->ToString().String(),
1479 					pass);
1480 				delete condition;
1481 			} else
1482 				debug_printf("Could not parse condition!\n");
1483 		}
1484 
1485 		if (pass) {
1486 			_AddRunTargets(runMessage, NULL);
1487 			_AddRunTargets(runMessage, "then");
1488 		} else {
1489 			_AddRunTargets(runMessage, "else");
1490 		}
1491 	}
1492 }
1493 
1494 
1495 void
1496 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name)
1497 {
1498 	BMessage targets;
1499 	if (name != NULL && message.FindMessage(name, &targets) != B_OK)
1500 		return;
1501 
1502 	const char* target;
1503 	for (int32 index = 0; targets.FindString("target", index, &target) == B_OK;
1504 			index++) {
1505 		fRunTargets.Add(target);
1506 	}
1507 }
1508 
1509 
1510 void
1511 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message)
1512 {
1513 	BString name = message.GetString("name");
1514 	if (name.IsEmpty()) {
1515 		// Invalid job description
1516 		return;
1517 	}
1518 	name.ToLower();
1519 
1520 	Job* job = FindJob(name);
1521 	if (job == NULL) {
1522 		TRACE("  add job \"%s\"\n", name.String());
1523 
1524 		job = new (std::nothrow) Job(name);
1525 		if (job == NULL)
1526 			return;
1527 
1528 		job->SetTeamListener(this);
1529 		job->SetService(service);
1530 		job->SetCreateDefaultPort(service);
1531 		job->SetTarget(target);
1532 	} else
1533 		TRACE("  amend job \"%s\"\n", name.String());
1534 
1535 	if (message.HasBool("disabled")) {
1536 		job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled()));
1537 		fLog.JobEnabled(job, job->IsEnabled());
1538 	}
1539 
1540 	if (message.HasBool("legacy"))
1541 		job->SetCreateDefaultPort(!message.GetBool("legacy", !service));
1542 
1543 	_SetCondition(job, message);
1544 	_SetEvent(job, message);
1545 	_SetEnvironment(job, message);
1546 
1547 	BMessage portMessage;
1548 	for (int32 index = 0;
1549 			message.FindMessage("port", index, &portMessage) == B_OK; index++) {
1550 		job->AddPort(portMessage);
1551 	}
1552 
1553 	if (message.HasString("launch"))
1554 		message.FindStrings("launch", &job->Arguments());
1555 
1556 	const char* requirement;
1557 	for (int32 index = 0;
1558 			message.FindString("requires", index, &requirement) == B_OK;
1559 			index++) {
1560 		job->AddRequirement(requirement);
1561 	}
1562 	if (fInitTarget != NULL)
1563 		job->AddRequirement(fInitTarget->Name());
1564 
1565 	fJobs.insert(std::make_pair(job->Title(), job));
1566 }
1567 
1568 
1569 /*!	Initializes all jobs for the specified target (may be \c NULL).
1570 	Jobs that cannot be initialized, and those that never will be due to
1571 	conditions, will be removed from the list.
1572 */
1573 void
1574 LaunchDaemon::_InitJobs(Target* target)
1575 {
1576 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) {
1577 		Job* job = iterator->second;
1578 		JobMap::iterator remove = iterator++;
1579 
1580 		if (job->Target() != target)
1581 			continue;
1582 
1583 		status_t status = B_NO_INIT;
1584 		if (job->IsEnabled()) {
1585 			// Filter out jobs that have a constant and failing condition
1586 			if (job->Condition() == NULL || !job->Condition()->IsConstant(*this)
1587 				|| job->Condition()->Test(*this)) {
1588 				std::set<BString> dependencies;
1589 				status = job->Init(*this, dependencies);
1590 				if (status == B_OK && job->Event() != NULL)
1591 					status = job->Event()->Register(*this);
1592 			}
1593 		}
1594 
1595 		if (status == B_OK) {
1596 			fLog.JobInitialized(job);
1597 		} else {
1598 			if (status != B_NO_INIT) {
1599 				// TODO: log error
1600 				debug_printf("Init \"%s\" failed: %s\n", job->Name(),
1601 					strerror(status));
1602 			}
1603 			fLog.JobIgnored(job, status);
1604 
1605 			// Remove jobs that won't be used later on
1606 			fJobs.erase(remove);
1607 			delete job;
1608 		}
1609 	}
1610 }
1611 
1612 
1613 /*!	Adds all jobs for the specified target (may be \c NULL) to the launch
1614 	queue, except those that are triggered by events that haven't been
1615 	triggered yet.
1616 
1617 	Unless \a forceNow is true, the target is only launched if its events,
1618 	if any, have been triggered already, and its conditions are met.
1619 */
1620 void
1621 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow)
1622 {
1623 	if (!forceNow && target != NULL && (!target->EventHasTriggered()
1624 		|| !target->CheckCondition(*this))) {
1625 		return;
1626 	}
1627 
1628 	if (target != NULL && !target->HasLaunched()) {
1629 		target->SetLaunched(true);
1630 		_InitJobs(target);
1631 	}
1632 
1633 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1634 			iterator++) {
1635 		Job* job = iterator->second;
1636 		if (job->Target() == target)
1637 			_LaunchJob(job);
1638 	}
1639 }
1640 
1641 
1642 /*!	Stops all running jobs of the specified target (may be \c NULL).
1643 */
1644 void
1645 LaunchDaemon::_StopJobs(Target* target, bool force)
1646 {
1647 	if (target != NULL && !target->HasLaunched())
1648 		return;
1649 
1650 	for (JobMap::reverse_iterator iterator = fJobs.rbegin();
1651 			iterator != fJobs.rend(); iterator++) {
1652 		Job* job = iterator->second;
1653 		if (job->Target() == target)
1654 			_StopJob(job, force);
1655 	}
1656 }
1657 
1658 
1659 /*!	Checks whether or not the specified \a job can be launched.
1660 	If \a testOnly is \c false, calling this method will trigger a demand
1661 	to the \a job.
1662 */
1663 bool
1664 LaunchDaemon::_CanLaunchJob(Job* job, uint32 options, bool testOnly)
1665 {
1666 	if (job == NULL || !job->CanBeLaunched())
1667 		return false;
1668 
1669 	return (options & FORCE_NOW) != 0
1670 		|| (job->EventHasTriggered() && job->CheckCondition(*this)
1671 			&& ((options & TRIGGER_DEMAND) == 0
1672 				|| Events::TriggerDemand(job->Event(), testOnly)));
1673 }
1674 
1675 
1676 /*!	Checks recursively if the requirements of the specified job can be launched,
1677 	if they are not running already.
1678 	Calling this method will not trigger a demand for the requirements.
1679 */
1680 bool
1681 LaunchDaemon::_CanLaunchJobRequirements(Job* job, uint32 options)
1682 {
1683 	int32 count = job->Requirements().CountStrings();
1684 	for (int32 index = 0; index < count; index++) {
1685 		Job* requirement = FindJob(job->Requirements().StringAt(index));
1686 		if (requirement != NULL
1687 			&& !requirement->IsRunning() && !requirement->IsLaunching()
1688 			&& (!_CanLaunchJob(requirement, options, true)
1689 				|| _CanLaunchJobRequirements(requirement, options))) {
1690 			requirement->AddPending(job->Name());
1691 			return false;
1692 		}
1693 	}
1694 
1695 	return true;
1696 }
1697 
1698 
1699 /*!	Adds the specified \a job to the launch queue
1700 	queue, except those that are triggered by events.
1701 
1702 	Unless \c FORCE_NOW is set, the target is only launched if its events,
1703 	if any, have been triggered already.
1704 
1705 	Calling this method will trigger a demand event if \c TRIGGER_DEMAND has
1706 	been set.
1707 */
1708 bool
1709 LaunchDaemon::_LaunchJob(Job* job, uint32 options)
1710 {
1711 	if (job != NULL && (job->IsLaunching() || job->IsRunning()))
1712 		return true;
1713 
1714 	if (!_CanLaunchJob(job, options))
1715 		return false;
1716 
1717 	// Test if we can launch all requirements
1718 	if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND))
1719 		return false;
1720 
1721 	// Actually launch the requirements
1722 	int32 count = job->Requirements().CountStrings();
1723 	for (int32 index = 0; index < count; index++) {
1724 		Job* requirement = FindJob(job->Requirements().StringAt(index));
1725 		if (requirement != NULL) {
1726 			// TODO: For jobs that have their communication channels set up,
1727 			// we would not need to trigger demand at this point
1728 			if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) {
1729 				// Failed to put a requirement into the launch queue
1730 				return false;
1731 			}
1732 		}
1733 	}
1734 
1735 	if (job->Target() != NULL)
1736 		job->Target()->ResolveSourceFiles();
1737 	if (job->Event() != NULL)
1738 		job->Event()->ResetTrigger();
1739 
1740 	job->SetLaunching(true);
1741 
1742 	status_t status = fJobQueue.AddJob(job);
1743 	if (status != B_OK) {
1744 		debug_printf("Adding job %s to queue failed: %s\n", job->Name(),
1745 			strerror(status));
1746 		return false;
1747 	}
1748 
1749 	// Try to launch pending jobs as well
1750 	count = job->Pending().CountStrings();
1751 	for (int32 index = 0; index < count; index++) {
1752 		Job* pending = FindJob(job->Pending().StringAt(index));
1753 		if (pending != NULL && _LaunchJob(pending, 0)) {
1754 			// Remove the job from the pending list once its in the launch
1755 			// queue, so that is not being launched again next time.
1756 			index--;
1757 			count--;
1758 		}
1759 	}
1760 
1761 	return true;
1762 }
1763 
1764 
1765 void
1766 LaunchDaemon::_StopJob(Job* job, bool force)
1767 {
1768 	// TODO: find out which jobs require this job, and don't stop if any,
1769 	// unless force, and then stop them all.
1770 	job->SetEnabled(false);
1771 
1772 	if (!job->IsRunning())
1773 		return;
1774 
1775 	// Be nice first, and send a simple quit message
1776 	BMessenger messenger;
1777 	if (job->GetMessenger(messenger) == B_OK) {
1778 		BMessage request(B_QUIT_REQUESTED);
1779 		messenger.SendMessage(&request);
1780 
1781 		// TODO: wait a bit before going further
1782 		return;
1783 	}
1784 	// TODO: allow custom shutdown
1785 
1786 	send_signal(-job->Team(), SIGINT);
1787 	// TODO: this would be the next step, again, after a delay
1788 	//send_signal(job->Team(), SIGKILL);
1789 }
1790 
1791 
1792 void
1793 LaunchDaemon::_AddTarget(Target* target)
1794 {
1795 	fTargets.insert(std::make_pair(target->Title(), target));
1796 }
1797 
1798 
1799 void
1800 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message)
1801 {
1802 	Condition* condition = job->Condition();
1803 	bool updated = false;
1804 
1805 	BMessage conditions;
1806 	if (message.FindMessage("if", &conditions) == B_OK) {
1807 		condition = Conditions::FromMessage(conditions);
1808 		updated = true;
1809 	}
1810 
1811 	if (message.GetBool("no_safemode")) {
1812 		condition = Conditions::AddNotSafeMode(condition);
1813 		updated = true;
1814 	}
1815 
1816 	if (updated)
1817 		job->SetCondition(condition);
1818 }
1819 
1820 
1821 void
1822 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message)
1823 {
1824 	Event* event = job->Event();
1825 	bool updated = false;
1826 
1827 	BMessage events;
1828 	if (message.FindMessage("on", &events) == B_OK) {
1829 		event = Events::FromMessage(this, events);
1830 		updated = true;
1831 	}
1832 
1833 	if (message.GetBool("on_demand")) {
1834 		event = Events::AddOnDemand(this, event);
1835 		updated = true;
1836 	}
1837 
1838 	if (updated) {
1839 		TRACE("    event: %s\n", event->ToString().String());
1840 		job->SetEvent(event);
1841 		_ResolveExternalEvents(job);
1842 	}
1843 }
1844 
1845 
1846 void
1847 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message)
1848 {
1849 	BMessage environmentMessage;
1850 	if (message.FindMessage("env", &environmentMessage) == B_OK)
1851 		job->SetEnvironment(environmentMessage);
1852 }
1853 
1854 
1855 ExternalEventSource*
1856 LaunchDaemon::_FindEvent(const char* owner, const char* name) const
1857 {
1858 	if (name == NULL)
1859 		return NULL;
1860 
1861 	BString eventName = name;
1862 	eventName.ToLower();
1863 
1864 	EventMap::const_iterator found = fEvents.find(eventName);
1865 	if (found != fEvents.end())
1866 		return found->second;
1867 
1868 	if (owner == NULL)
1869 		return NULL;
1870 
1871 	eventName.Prepend("/");
1872 	eventName.Prepend(get_leaf(owner));
1873 
1874 	found = fEvents.find(eventName);
1875 	if (found != fEvents.end())
1876 		return found->second;
1877 
1878 	return NULL;
1879 }
1880 
1881 
1882 void
1883 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event,
1884 	const BString& name)
1885 {
1886 	for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1887 			iterator++) {
1888 		Job* job = iterator->second;
1889 		if (Events::ResolveExternalEvent(job->Event(), name, event->Flags()))
1890 			event->AddListener(job);
1891 	}
1892 }
1893 
1894 
1895 void
1896 LaunchDaemon::_ResolveExternalEvents(BaseJob* job)
1897 {
1898 	if (job->Event() == NULL)
1899 		return;
1900 
1901 	for (EventMap::iterator iterator = fEvents.begin();
1902 			iterator != fEvents.end(); iterator++) {
1903 		ExternalEventSource* event = iterator->second;
1904 		if (Events::ResolveExternalEvent(job->Event(), event->Name(),
1905 				event->Flags()))
1906 			event->AddListener(job);
1907 	}
1908 }
1909 
1910 
1911 void
1912 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info)
1913 {
1914 	info.SetString("name", job->Name());
1915 
1916 	if (job->Event() != NULL)
1917 		info.SetString("event", job->Event()->ToString());
1918 
1919 	if (job->Condition() != NULL)
1920 		info.SetString("condition", job->Condition()->ToString());
1921 }
1922 
1923 
1924 void
1925 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message)
1926 {
1927 	if (fUserMode)
1928 		return;
1929 
1930 	// Forward event to user launch_daemon(s)
1931 	if (user == 0) {
1932 		for (SessionMap::iterator iterator = fSessions.begin();
1933 				iterator != fSessions.end(); iterator++) {
1934 			Session* session = iterator->second;
1935 			session->Daemon().SendMessage(message);
1936 				// ignore reply
1937 		}
1938 	} else {
1939 		Session* session = FindSession(user);
1940 		if (session != NULL)
1941 			session->Daemon().SendMessage(message);
1942 	}
1943 }
1944 
1945 
1946 status_t
1947 LaunchDaemon::_StartSession(const char* login)
1948 {
1949 	// TODO: enable user/group code
1950 	// The launch_daemon currently cannot talk to the registrar, though
1951 
1952 	struct passwd* passwd = getpwnam(login);
1953 	if (passwd == NULL)
1954 		return B_NAME_NOT_FOUND;
1955 	if (strcmp(passwd->pw_name, login) != 0)
1956 		return B_NAME_NOT_FOUND;
1957 
1958 	// Check if there is a user session running already
1959 	uid_t user = passwd->pw_uid;
1960 	gid_t group = passwd->pw_gid;
1961 
1962 	Unlock();
1963 
1964 	if (fork() == 0) {
1965 		if (setsid() < 0)
1966 			exit(EXIT_FAILURE);
1967 
1968 		if (initgroups(login, group) == -1)
1969 			exit(EXIT_FAILURE);
1970 		if (setgid(group) != 0)
1971 			exit(EXIT_FAILURE);
1972 		if (setuid(user) != 0)
1973 			exit(EXIT_FAILURE);
1974 
1975 		if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') {
1976 			setenv("HOME", passwd->pw_dir, true);
1977 
1978 			if (chdir(passwd->pw_dir) != 0) {
1979 				debug_printf("Could not switch to home dir %s: %s\n",
1980 					passwd->pw_dir, strerror(errno));
1981 			}
1982 		}
1983 
1984 		// TODO: This leaks the parent application
1985 		be_app = NULL;
1986 
1987 		// Reinitialize be_roster
1988 		BRoster::Private().DeleteBeRoster();
1989 		BRoster::Private().InitBeRoster();
1990 
1991 		// TODO: take over system jobs, and reserve their names (or ask parent)
1992 		status_t status;
1993 		LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status);
1994 		if (status == B_OK)
1995 			daemon->Run();
1996 
1997 		delete daemon;
1998 		exit(EXIT_SUCCESS);
1999 	}
2000 	Lock();
2001 	return B_OK;
2002 }
2003 
2004 
2005 void
2006 LaunchDaemon::_RetrieveKernelOptions()
2007 {
2008 	char buffer[32];
2009 	size_t size = sizeof(buffer);
2010 	status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer,
2011 		&size);
2012 	if (status == B_OK) {
2013 		fSafeMode = !strncasecmp(buffer, "true", size)
2014 			|| !strncasecmp(buffer, "yes", size)
2015 			|| !strncasecmp(buffer, "on", size)
2016 			|| !strncasecmp(buffer, "enabled", size);
2017 	} else
2018 		fSafeMode = false;
2019 }
2020 
2021 
2022 void
2023 LaunchDaemon::_SetupEnvironment()
2024 {
2025 	// Determine safemode kernel option
2026 	setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true);
2027 
2028 	// Default locale settings
2029 	setenv("LC_TYPE", "en_US.UTF-8", true);
2030 }
2031 
2032 
2033 /*!	Basic system initialization that must happen before any jobs are launched.
2034 */
2035 void
2036 LaunchDaemon::_InitSystem()
2037 {
2038 #ifndef TEST_MODE
2039 	_AddInitJob(new InitRealTimeClockJob());
2040 	_AddInitJob(new InitSharedMemoryDirectoryJob());
2041 	_AddInitJob(new InitTemporaryDirectoryJob());
2042 #endif
2043 
2044 	fJobQueue.AddJob(fInitTarget);
2045 }
2046 
2047 
2048 void
2049 LaunchDaemon::_AddInitJob(BJob* job)
2050 {
2051 	fInitTarget->AddDependency(job);
2052 	fJobQueue.AddJob(job);
2053 }
2054 
2055 
2056 // #pragma mark -
2057 
2058 
2059 #ifndef TEST_MODE
2060 
2061 
2062 static void
2063 open_stdio(int targetFD, int openMode)
2064 {
2065 #ifdef DEBUG
2066 	int fd = open("/dev/dprintf", openMode);
2067 #else
2068 	int fd = open("/dev/null", openMode);
2069 #endif
2070 	if (fd != targetFD) {
2071 		dup2(fd, targetFD);
2072 		close(fd);
2073 	}
2074 }
2075 
2076 
2077 #endif	// TEST_MODE
2078 
2079 
2080 int
2081 main()
2082 {
2083 	if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) {
2084 		fprintf(stderr, "The launch_daemon is already running!\n");
2085 		return EXIT_FAILURE;
2086 	}
2087 
2088 #ifndef TEST_MODE
2089 	// Make stdin/out/err available
2090 	open_stdio(STDIN_FILENO, O_RDONLY);
2091 	open_stdio(STDOUT_FILENO, O_WRONLY);
2092 	dup2(STDOUT_FILENO, STDERR_FILENO);
2093 #endif
2094 
2095 	EventMap events;
2096 	status_t status;
2097 	LaunchDaemon* daemon = new LaunchDaemon(false, events, status);
2098 	if (status == B_OK)
2099 		daemon->Run();
2100 
2101 	delete daemon;
2102 	return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;
2103 }
2104