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