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