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