Make UEFI boot-platform build again
[haiku.git] / src / servers / launch / LaunchDaemon.cpp
blobc1a1418a1eb4db31bfb7147718742b8d18c2d5f9
1 /*
2 * Copyright 2015-2017, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include "LaunchDaemon.h"
9 #include <map>
10 #include <set>
12 #include <errno.h>
13 #include <grp.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
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>
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>
36 #include "multiuser_utils.h"
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"
50 #ifdef DEBUG
51 # define TRACE(x, ...) debug_printf(x, __VA_ARGS__)
52 #else
53 # define TRACE(x, ...) ;
54 #endif
57 using namespace ::BPrivate;
58 using namespace BSupportKit;
59 using BSupportKit::BPrivate::JobQueue;
62 #ifndef TEST_MODE
63 static const char* kLaunchDirectory = "launch";
64 static const char* kUserLaunchDirectory = "user_launch";
65 #endif
68 enum launch_options {
69 FORCE_NOW = 0x01,
70 TRIGGER_DEMAND = 0x02
74 class Session {
75 public:
76 Session(uid_t user, const BMessenger& target);
78 uid_t User() const
79 { return fUser; }
80 const BMessenger& Daemon() const
81 { return fDaemon; }
83 private:
84 uid_t fUser;
85 BMessenger fDaemon;
89 /*! This class is the connection between the external events that are part of
90 a job, and the external event source.
92 There is one object per registered event source, and it keeps all jobs that
93 reference the event as listeners. If the event source triggers the event,
94 the object will be used to trigger the jobs.
96 class ExternalEventSource {
97 public:
98 ExternalEventSource(BMessenger& source,
99 const char* ownerName,
100 const char* name, uint32 flags);
101 ~ExternalEventSource();
103 const char* Name() const;
104 uint32 Flags() const
105 { return fFlags; }
107 int32 CountListeners() const;
108 BaseJob* ListenerAt(int32 index) const;
110 status_t AddListener(BaseJob* job);
111 void RemoveListener(BaseJob* job);
113 private:
114 BString fName;
115 uint32 fFlags;
116 BObjectList<BaseJob> fListeners;
120 typedef std::map<BString, Job*> JobMap;
121 typedef std::map<uid_t, Session*> SessionMap;
122 typedef std::map<BString, Target*> TargetMap;
123 typedef std::map<BString, ExternalEventSource*> EventMap;
124 typedef std::map<team_id, Job*> TeamMap;
127 class LaunchDaemon : public BServer, public Finder, public ConditionContext,
128 public EventRegistrator, public TeamRegistrator {
129 public:
130 LaunchDaemon(bool userMode,
131 const EventMap& events, status_t& error);
132 virtual ~LaunchDaemon();
134 virtual Job* FindJob(const char* name) const;
135 virtual Target* FindTarget(const char* name) const;
136 Session* FindSession(uid_t user) const;
138 // ConditionContext
139 virtual bool IsSafeMode() const;
140 virtual bool BootVolumeIsReadOnly() const;
142 // EventRegistrator
143 virtual status_t RegisterExternalEvent(Event* event,
144 const char* name,
145 const BStringList& arguments);
146 virtual void UnregisterExternalEvent(Event* event,
147 const char* name);
149 // TeamRegistrator
150 virtual void RegisterTeam(Job* job);
152 virtual void ReadyToRun();
153 virtual void MessageReceived(BMessage* message);
155 private:
156 void _HandleGetLaunchData(BMessage* message);
157 void _HandleLaunchTarget(BMessage* message);
158 void _HandleStopLaunchTarget(BMessage* message);
159 void _HandleLaunchJob(BMessage* message);
160 void _HandleEnableLaunchJob(BMessage* message);
161 void _HandleStopLaunchJob(BMessage* message);
162 void _HandleLaunchSession(BMessage* message);
163 void _HandleRegisterSessionDaemon(BMessage* message);
164 void _HandleRegisterLaunchEvent(BMessage* message);
165 void _HandleUnregisterLaunchEvent(BMessage* message);
166 void _HandleNotifyLaunchEvent(BMessage* message);
167 void _HandleResetStickyLaunchEvent(
168 BMessage* message);
169 void _HandleGetLaunchTargets(BMessage* message);
170 void _HandleGetLaunchTargetInfo(BMessage* message);
171 void _HandleGetLaunchJobs(BMessage* message);
172 void _HandleGetLaunchJobInfo(BMessage* message);
173 uid_t _GetUserID(BMessage* message);
175 void _ReadPaths(const BStringList& paths);
176 void _ReadEntry(const char* context, BEntry& entry);
177 void _ReadDirectory(const char* context,
178 BEntry& directory);
179 status_t _ReadFile(const char* context, BEntry& entry);
181 void _AddJobs(Target* target, BMessage& message);
182 void _AddTargets(BMessage& message);
183 void _AddRunTargets(BMessage& message);
184 void _AddRunTargets(BMessage& message,
185 const char* name);
186 void _AddJob(Target* target, bool service,
187 BMessage& message);
188 void _InitJobs(Target* target);
189 void _LaunchJobs(Target* target,
190 bool forceNow = false);
191 void _StopJobs(Target* target, bool force);
192 bool _CanLaunchJob(Job* job, uint32 options,
193 bool testOnly = false);
194 bool _CanLaunchJobRequirements(Job* job,
195 uint32 options);
196 bool _LaunchJob(Job* job, uint32 options = 0);
197 void _StopJob(Job* job, bool force);
198 void _AddTarget(Target* target);
199 void _SetCondition(BaseJob* job,
200 const BMessage& message);
201 void _SetEvent(BaseJob* job,
202 const BMessage& message);
203 void _SetEnvironment(BaseJob* job,
204 const BMessage& message);
206 ExternalEventSource*
207 _FindEvent(const char* owner,
208 const char* name) const;
209 void _ResolveExternalEvents(
210 ExternalEventSource* event,
211 const BString& name);
212 void _ResolveExternalEvents(BaseJob* job);
213 void _GetBaseJobInfo(BaseJob* job, BMessage& info);
214 void _ForwardEventMessage(uid_t user,
215 BMessage* message);
217 status_t _StartSession(const char* login);
219 void _RetrieveKernelOptions();
220 void _SetupEnvironment();
221 void _InitSystem();
222 void _AddInitJob(BJob* job);
224 private:
225 JobMap fJobs;
226 TargetMap fTargets;
227 BStringList fRunTargets;
228 EventMap fEvents;
229 JobQueue fJobQueue;
230 SessionMap fSessions;
231 MainWorker* fMainWorker;
232 Target* fInitTarget;
233 TeamMap fTeams;
234 mutex fTeamsLock;
235 bool fSafeMode;
236 bool fReadOnlyBootVolume;
237 bool fUserMode;
241 static const char*
242 get_leaf(const char* signature)
244 if (signature == NULL)
245 return NULL;
247 const char* separator = strrchr(signature, '/');
248 if (separator != NULL)
249 return separator + 1;
251 return signature;
255 // #pragma mark -
258 Session::Session(uid_t user, const BMessenger& daemon)
260 fUser(user),
261 fDaemon(daemon)
266 // #pragma mark -
269 ExternalEventSource::ExternalEventSource(BMessenger& source,
270 const char* ownerName, const char* name, uint32 flags)
272 fName(name),
273 fFlags(flags),
274 fListeners(5, true)
279 ExternalEventSource::~ExternalEventSource()
284 const char*
285 ExternalEventSource::Name() const
287 return fName.String();
291 int32
292 ExternalEventSource::CountListeners() const
294 return fListeners.CountItems();
298 BaseJob*
299 ExternalEventSource::ListenerAt(int32 index) const
301 return fListeners.ItemAt(index);
305 status_t
306 ExternalEventSource::AddListener(BaseJob* job)
308 if (fListeners.AddItem(job))
309 return B_OK;
311 return B_NO_MEMORY;
315 void
316 ExternalEventSource::RemoveListener(BaseJob* job)
318 fListeners.RemoveItem(job);
322 // #pragma mark -
325 LaunchDaemon::LaunchDaemon(bool userMode, const EventMap& events,
326 status_t& error)
328 BServer(kLaunchDaemonSignature, NULL,
329 create_port(B_LOOPER_PORT_DEFAULT_CAPACITY,
330 userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error),
331 fEvents(events),
332 fInitTarget(userMode ? NULL : new Target("init")),
333 #ifdef TEST_MODE
334 fUserMode(true)
335 #else
336 fUserMode(userMode)
337 #endif
339 mutex_init(&fTeamsLock, "teams lock");
341 fMainWorker = new MainWorker(fJobQueue);
342 fMainWorker->Init();
344 if (fInitTarget != NULL)
345 _AddTarget(fInitTarget);
347 // We may not be able to talk to the registrar
348 if (!fUserMode)
349 BRoster::Private().SetWithoutRegistrar(true);
353 LaunchDaemon::~LaunchDaemon()
358 Job*
359 LaunchDaemon::FindJob(const char* name) const
361 if (name == NULL)
362 return NULL;
364 JobMap::const_iterator found = fJobs.find(BString(name).ToLower());
365 if (found != fJobs.end())
366 return found->second;
368 return NULL;
372 Target*
373 LaunchDaemon::FindTarget(const char* name) const
375 if (name == NULL)
376 return NULL;
378 TargetMap::const_iterator found = fTargets.find(BString(name).ToLower());
379 if (found != fTargets.end())
380 return found->second;
382 return NULL;
386 Session*
387 LaunchDaemon::FindSession(uid_t user) const
389 SessionMap::const_iterator found = fSessions.find(user);
390 if (found != fSessions.end())
391 return found->second;
393 return NULL;
397 bool
398 LaunchDaemon::IsSafeMode() const
400 return fSafeMode;
404 bool
405 LaunchDaemon::BootVolumeIsReadOnly() const
407 return fReadOnlyBootVolume;
411 status_t
412 LaunchDaemon::RegisterExternalEvent(Event* event, const char* name,
413 const BStringList& arguments)
415 // TODO: register actual event with event source
416 return B_OK;
420 void
421 LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name)
423 // TODO!
427 void
428 LaunchDaemon::RegisterTeam(Job* job)
430 MutexLocker locker(fTeamsLock);
431 fTeams.insert(std::make_pair(job->Team(), job));
435 void
436 LaunchDaemon::ReadyToRun()
438 _RetrieveKernelOptions();
439 _SetupEnvironment();
441 fReadOnlyBootVolume = Utility::IsReadOnlyVolume("/boot");
442 if (fReadOnlyBootVolume)
443 Utility::BlockMedia("/boot", true);
445 if (fUserMode) {
446 #ifndef TEST_MODE
447 BLaunchRoster roster;
448 BLaunchRoster::Private(roster).RegisterSessionDaemon(this);
449 #endif // TEST_MODE
450 } else
451 _InitSystem();
453 BStringList paths;
454 #ifdef TEST_MODE
455 paths.Add("/boot/home/test_launch");
456 #else
457 if (fUserMode) {
458 // System-wide user specific jobs
459 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kUserLaunchDirectory,
460 B_FIND_PATHS_SYSTEM_ONLY, paths);
461 _ReadPaths(paths);
464 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory,
465 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
466 _ReadPaths(paths);
468 if (fUserMode) {
469 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY,
470 kUserLaunchDirectory, B_FIND_PATHS_SYSTEM_ONLY, paths);
471 _ReadPaths(paths);
474 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory,
475 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths);
476 #endif // TEST_MODE
477 _ReadPaths(paths);
479 BMessenger target(this);
480 BMessenger::Private messengerPrivate(target);
481 port_id port = messengerPrivate.Port();
482 int32 token = messengerPrivate.Token();
483 __start_watching_system(-1, B_WATCH_SYSTEM_TEAM_DELETION, port, token);
485 _InitJobs(NULL);
486 _LaunchJobs(NULL);
488 // Launch run targets (ignores events)
489 for (int32 index = 0; index < fRunTargets.CountStrings(); index++) {
490 Target* target = FindTarget(fRunTargets.StringAt(index));
491 if (target != NULL)
492 _LaunchJobs(target);
495 if (fUserMode)
496 be_roster->StartWatching(this, B_REQUEST_LAUNCHED);
500 void
501 LaunchDaemon::MessageReceived(BMessage* message)
503 switch (message->what) {
504 case B_SYSTEM_OBJECT_UPDATE:
506 int32 opcode = message->GetInt32("opcode", 0);
507 team_id team = (team_id)message->GetInt32("team", -1);
508 if (opcode != B_TEAM_DELETED || team < 0)
509 break;
511 MutexLocker locker(fTeamsLock);
513 TeamMap::iterator found = fTeams.find(team);
514 if (found != fTeams.end()) {
515 Job* job = found->second;
516 TRACE("Job %s ended!\n", job->Name());
517 job->TeamDeleted();
519 if (job->IsService()) {
520 // TODO: take restart throttle into account
521 // TODO: don't restart on shutdown
522 _LaunchJob(job);
525 break;
527 case B_SOME_APP_LAUNCHED:
529 team_id team = (team_id)message->GetInt32("be:team", -1);
530 Job* job = NULL;
532 MutexLocker locker(fTeamsLock);
534 TeamMap::iterator found = fTeams.find(team);
535 if (found != fTeams.end()) {
536 job = found->second;
537 locker.Unlock();
538 } else {
539 locker.Unlock();
541 // Find job by name instead
542 const char* signature = message->GetString("be:signature");
543 job = FindJob(get_leaf(signature));
544 if (job != NULL) {
545 TRACE("Updated default port of untracked team %d, %s\n",
546 (int)team, signature);
550 if (job != NULL) {
551 // Update port info
552 app_info info;
553 status_t status = be_roster->GetRunningAppInfo(team, &info);
554 if (status == B_OK && info.port != job->DefaultPort()) {
555 TRACE("Update default port for %s to %d\n", job->Name(),
556 (int)info.port);
557 job->SetDefaultPort(info.port);
560 break;
563 case B_GET_LAUNCH_DATA:
564 _HandleGetLaunchData(message);
565 break;
567 case B_LAUNCH_TARGET:
568 _HandleLaunchTarget(message);
569 break;
570 case B_STOP_LAUNCH_TARGET:
571 _HandleStopLaunchTarget(message);
572 break;
573 case B_LAUNCH_JOB:
574 _HandleLaunchJob(message);
575 break;
576 case B_ENABLE_LAUNCH_JOB:
577 _HandleEnableLaunchJob(message);
578 break;
579 case B_STOP_LAUNCH_JOB:
580 _HandleStopLaunchJob(message);
581 break;
583 case B_LAUNCH_SESSION:
584 _HandleLaunchSession(message);
585 break;
586 case B_REGISTER_SESSION_DAEMON:
587 _HandleRegisterSessionDaemon(message);
588 break;
590 case B_REGISTER_LAUNCH_EVENT:
591 _HandleRegisterLaunchEvent(message);
592 break;
593 case B_UNREGISTER_LAUNCH_EVENT:
594 _HandleUnregisterLaunchEvent(message);
595 break;
596 case B_NOTIFY_LAUNCH_EVENT:
597 _HandleNotifyLaunchEvent(message);
598 break;
599 case B_RESET_STICKY_LAUNCH_EVENT:
600 _HandleResetStickyLaunchEvent(message);
601 break;
603 case B_GET_LAUNCH_TARGETS:
604 _HandleGetLaunchTargets(message);
605 break;
606 case B_GET_LAUNCH_TARGET_INFO:
607 _HandleGetLaunchTargetInfo(message);
608 break;
609 case B_GET_LAUNCH_JOBS:
610 _HandleGetLaunchJobs(message);
611 break;
612 case B_GET_LAUNCH_JOB_INFO:
613 _HandleGetLaunchJobInfo(message);
614 break;
616 case kMsgEventTriggered:
618 // An internal event has been triggered.
619 // Check if its job can be launched now.
620 const char* name = message->GetString("owner");
621 if (name == NULL)
622 break;
624 Job* job = FindJob(name);
625 if (job != NULL) {
626 _LaunchJob(job);
627 break;
630 Target* target = FindTarget(name);
631 if (target != NULL) {
632 _LaunchJobs(target);
633 break;
635 break;
638 default:
639 BServer::MessageReceived(message);
640 break;
645 void
646 LaunchDaemon::_HandleGetLaunchData(BMessage* message)
648 uid_t user = _GetUserID(message);
649 if (user < 0)
650 return;
652 BMessage reply((uint32)B_OK);
653 bool launchJob = true;
655 Job* job = FindJob(get_leaf(message->GetString("name")));
656 if (job == NULL) {
657 Session* session = FindSession(user);
658 if (session != NULL) {
659 // Forward request to user launch_daemon
660 if (session->Daemon().SendMessage(message) == B_OK)
661 return;
663 reply.what = B_NAME_NOT_FOUND;
664 } else if (job->IsService() && !job->IsLaunched()) {
665 if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) {
666 // The job exists, but cannot be started yet, as its
667 // conditions are not met; don't make it available yet
668 // TODO: we may not want to initialize jobs with conditions
669 // that aren't met yet
670 reply.what = B_NO_INIT;
671 } else if (job->Event() != NULL) {
672 if (!Events::TriggerDemand(job->Event())) {
673 // The job is not triggered by demand; we cannot start it now
674 reply.what = B_NO_INIT;
675 } else {
676 // The job has already been triggered, don't launch it again
677 launchJob = false;
680 } else
681 launchJob = false;
683 bool ownsMessage = false;
684 if (reply.what == B_OK) {
685 // Launch the job if it hasn't been launched already
686 if (launchJob)
687 _LaunchJob(job, TRIGGER_DEMAND);
689 DetachCurrentMessage();
690 status_t result = job->HandleGetLaunchData(message);
691 if (result == B_OK) {
692 // Replying is delegated to the job.
693 return;
696 ownsMessage = true;
697 reply.what = result;
700 message->SendReply(&reply);
701 if (ownsMessage)
702 delete message;
706 void
707 LaunchDaemon::_HandleLaunchTarget(BMessage* message)
709 uid_t user = _GetUserID(message);
710 if (user < 0)
711 return;
713 const char* name = message->GetString("target");
714 const char* baseName = message->GetString("base target");
716 Target* target = FindTarget(name);
717 if (target == NULL && baseName != NULL) {
718 Target* baseTarget = FindTarget(baseName);
719 if (baseTarget != NULL) {
720 target = new Target(name);
722 // Copy all jobs with the base target into the new target
723 for (JobMap::iterator iterator = fJobs.begin();
724 iterator != fJobs.end();) {
725 Job* job = iterator->second;
726 iterator++;
728 if (job->Target() == baseTarget) {
729 Job* copy = new Job(*job);
730 copy->SetTarget(target);
732 fJobs.insert(std::make_pair(copy->Name(), copy));
737 if (target == NULL) {
738 Session* session = FindSession(user);
739 if (session != NULL) {
740 // Forward request to user launch_daemon
741 if (session->Daemon().SendMessage(message) == B_OK)
742 return;
745 BMessage reply(B_NAME_NOT_FOUND);
746 message->SendReply(&reply);
747 return;
750 BMessage data;
751 if (message->FindMessage("data", &data) == B_OK)
752 target->AddData(data.GetString("name"), data);
754 _LaunchJobs(target);
756 BMessage reply((uint32)B_OK);
757 message->SendReply(&reply);
761 void
762 LaunchDaemon::_HandleStopLaunchTarget(BMessage* message)
764 uid_t user = _GetUserID(message);
765 if (user < 0)
766 return;
768 const char* name = message->GetString("target");
770 Target* target = FindTarget(name);
771 if (target == NULL) {
772 Session* session = FindSession(user);
773 if (session != NULL) {
774 // Forward request to user launch_daemon
775 if (session->Daemon().SendMessage(message) == B_OK)
776 return;
779 BMessage reply(B_NAME_NOT_FOUND);
780 message->SendReply(&reply);
781 return;
784 BMessage data;
785 if (message->FindMessage("data", &data) == B_OK)
786 target->AddData(data.GetString("name"), data);
788 _StopJobs(target, message->GetBool("force"));
790 BMessage reply((uint32)B_OK);
791 message->SendReply(&reply);
795 void
796 LaunchDaemon::_HandleLaunchJob(BMessage* message)
798 uid_t user = _GetUserID(message);
799 if (user < 0)
800 return;
802 const char* name = message->GetString("name");
804 Job* job = FindJob(name);
805 if (job == NULL) {
806 Session* session = FindSession(user);
807 if (session != NULL) {
808 // Forward request to user launch_daemon
809 if (session->Daemon().SendMessage(message) == B_OK)
810 return;
813 BMessage reply(B_NAME_NOT_FOUND);
814 message->SendReply(&reply);
815 return;
818 job->SetEnabled(true);
819 _LaunchJob(job, FORCE_NOW);
821 BMessage reply((uint32)B_OK);
822 message->SendReply(&reply);
826 void
827 LaunchDaemon::_HandleEnableLaunchJob(BMessage* message)
829 uid_t user = _GetUserID(message);
830 if (user < 0)
831 return;
833 const char* name = message->GetString("name");
834 bool enable = message->GetBool("enable");
836 Job* job = FindJob(name);
837 if (job == NULL) {
838 Session* session = FindSession(user);
839 if (session != NULL) {
840 // Forward request to user launch_daemon
841 if (session->Daemon().SendMessage(message) == B_OK)
842 return;
845 BMessage reply(B_NAME_NOT_FOUND);
846 message->SendReply(&reply);
847 return;
850 job->SetEnabled(enable);
852 BMessage reply((uint32)B_OK);
853 message->SendReply(&reply);
857 void
858 LaunchDaemon::_HandleStopLaunchJob(BMessage* message)
860 uid_t user = _GetUserID(message);
861 if (user < 0)
862 return;
864 const char* name = message->GetString("name");
866 Job* job = FindJob(name);
867 if (job == NULL) {
868 Session* session = FindSession(user);
869 if (session != NULL) {
870 // Forward request to user launch_daemon
871 if (session->Daemon().SendMessage(message) == B_OK)
872 return;
875 BMessage reply(B_NAME_NOT_FOUND);
876 message->SendReply(&reply);
877 return;
880 _StopJob(job, message->GetBool("force"));
882 BMessage reply((uint32)B_OK);
883 message->SendReply(&reply);
887 void
888 LaunchDaemon::_HandleLaunchSession(BMessage* message)
890 uid_t user = _GetUserID(message);
891 if (user < 0)
892 return;
894 status_t status = B_OK;
895 const char* login = message->GetString("login");
896 if (login == NULL)
897 status = B_BAD_VALUE;
898 if (status == B_OK && user != 0) {
899 // Only the root user can start sessions
900 // TODO: we'd actually need to know the uid of the sender
901 status = B_PERMISSION_DENIED;
903 if (status == B_OK)
904 status = _StartSession(login);
906 BMessage reply((uint32)status);
907 message->SendReply(&reply);
911 void
912 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message)
914 uid_t user = _GetUserID(message);
915 if (user < 0)
916 return;
918 status_t status = B_OK;
920 BMessenger target;
921 if (message->FindMessenger("daemon", &target) != B_OK)
922 status = B_BAD_VALUE;
924 if (status == B_OK) {
925 Session* session = new (std::nothrow) Session(user, target);
926 if (session != NULL)
927 fSessions.insert(std::make_pair(user, session));
928 else
929 status = B_NO_MEMORY;
932 BMessage reply((uint32)status);
933 message->SendReply(&reply);
937 void
938 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message)
940 uid_t user = _GetUserID(message);
941 if (user < 0)
942 return;
944 if (user == 0 || fUserMode) {
945 status_t status = B_OK;
947 const char* name = message->GetString("name");
948 const char* ownerName = message->GetString("owner");
949 uint32 flags = message->GetUInt32("flags", 0);
950 BMessenger source;
951 if (name != NULL && ownerName != NULL
952 && message->FindMessenger("source", &source) == B_OK) {
953 // Register event
954 ownerName = get_leaf(ownerName);
956 ExternalEventSource* event = new (std::nothrow)
957 ExternalEventSource(source, ownerName, name, flags);
958 if (event != NULL) {
959 // Use short name, and fully qualified name
960 BString eventName = name;
961 fEvents.insert(std::make_pair(eventName, event));
962 _ResolveExternalEvents(event, eventName);
964 eventName.Prepend("/");
965 eventName.Prepend(ownerName);
966 fEvents.insert(std::make_pair(eventName, event));
967 _ResolveExternalEvents(event, eventName);
968 } else
969 status = B_NO_MEMORY;
970 } else
971 status = B_BAD_VALUE;
973 BMessage reply((uint32)status);
974 message->SendReply(&reply);
977 _ForwardEventMessage(user, message);
981 void
982 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message)
984 uid_t user = _GetUserID(message);
985 if (user < 0)
986 return;
988 if (user == 0 || fUserMode) {
989 status_t status = B_OK;
991 const char* name = message->GetString("name");
992 const char* ownerName = message->GetString("owner");
993 BMessenger source;
994 if (name != NULL && ownerName != NULL
995 && message->FindMessenger("source", &source) == B_OK) {
996 // Unregister short and fully qualified event name
997 ownerName = get_leaf(ownerName);
999 BString eventName = name;
1000 fEvents.erase(eventName);
1002 eventName.Prepend("/");
1003 eventName.Prepend(ownerName);
1004 fEvents.erase(eventName);
1005 } else
1006 status = B_BAD_VALUE;
1008 BMessage reply((uint32)status);
1009 message->SendReply(&reply);
1012 _ForwardEventMessage(user, message);
1016 void
1017 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message)
1019 uid_t user = _GetUserID(message);
1020 if (user < 0)
1021 return;
1023 if (user == 0 || fUserMode) {
1024 // Trigger events
1025 const char* name = message->GetString("name");
1026 const char* ownerName = message->GetString("owner");
1027 // TODO: support arguments (as selectors)
1029 ExternalEventSource* event = _FindEvent(ownerName, name);
1030 if (event != NULL) {
1031 // Evaluate all of its jobs
1032 int32 count = event->CountListeners();
1033 for (int32 index = 0; index < count; index++) {
1034 BaseJob* listener = event->ListenerAt(index);
1035 Events::TriggerExternalEvent(listener->Event(), name);
1040 _ForwardEventMessage(user, message);
1044 void
1045 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message)
1047 uid_t user = _GetUserID(message);
1048 if (user < 0)
1049 return;
1051 if (user == 0 || fUserMode) {
1052 // Reset sticky events
1053 const char* name = message->GetString("name");
1054 const char* ownerName = message->GetString("owner");
1055 // TODO: support arguments (as selectors)
1057 ExternalEventSource* event = _FindEvent(ownerName, name);
1058 if (event != NULL) {
1059 // Evaluate all of its jobs
1060 int32 count = event->CountListeners();
1061 for (int32 index = 0; index < count; index++) {
1062 BaseJob* listener = event->ListenerAt(index);
1063 Events::ResetStickyExternalEvent(listener->Event(), name);
1068 _ForwardEventMessage(user, message);
1072 void
1073 LaunchDaemon::_HandleGetLaunchTargets(BMessage* message)
1075 uid_t user = _GetUserID(message);
1076 if (user < 0)
1077 return;
1079 BMessage reply;
1080 status_t status = B_OK;
1082 if (!fUserMode) {
1083 // Request the data from the user's daemon, too
1084 Session* session = FindSession(user);
1085 if (session != NULL) {
1086 BMessage request(B_GET_LAUNCH_TARGETS);
1087 status = request.AddInt32("user", 0);
1088 if (status == B_OK) {
1089 status = session->Daemon().SendMessage(&request,
1090 &reply);
1092 if (status == B_OK)
1093 status = reply.what;
1094 } else
1095 status = B_NAME_NOT_FOUND;
1098 if (status == B_OK) {
1099 TargetMap::const_iterator iterator = fTargets.begin();
1100 for (; iterator != fTargets.end(); iterator++)
1101 reply.AddString("target", iterator->first);
1104 reply.what = status;
1105 message->SendReply(&reply);
1109 void
1110 LaunchDaemon::_HandleGetLaunchTargetInfo(BMessage* message)
1112 uid_t user = _GetUserID(message);
1113 if (user < 0)
1114 return;
1116 const char* name = message->GetString("name");
1117 Target* target = FindTarget(name);
1118 if (target == NULL && !fUserMode) {
1119 _ForwardEventMessage(user, message);
1120 return;
1123 BMessage info(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND));
1124 if (target != NULL) {
1125 _GetBaseJobInfo(target, info);
1127 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1128 iterator++) {
1129 Job* job = iterator->second;
1130 if (job->Target() == target)
1131 info.AddString("job", job->Name());
1134 message->SendReply(&info);
1138 void
1139 LaunchDaemon::_HandleGetLaunchJobs(BMessage* message)
1141 uid_t user = _GetUserID(message);
1142 if (user < 0)
1143 return;
1145 const char* targetName = message->GetString("target");
1147 BMessage reply;
1148 status_t status = B_OK;
1150 if (!fUserMode) {
1151 // Request the data from the user's daemon, too
1152 Session* session = FindSession(user);
1153 if (session != NULL) {
1154 BMessage request(B_GET_LAUNCH_JOBS);
1155 status = request.AddInt32("user", 0);
1156 if (status == B_OK && targetName != NULL)
1157 status = request.AddString("target", targetName);
1158 if (status == B_OK) {
1159 status = session->Daemon().SendMessage(&request,
1160 &reply);
1162 if (status == B_OK)
1163 status = reply.what;
1164 } else
1165 status = B_NAME_NOT_FOUND;
1168 if (status == B_OK) {
1169 JobMap::const_iterator iterator = fJobs.begin();
1170 for (; iterator != fJobs.end(); iterator++) {
1171 Job* job = iterator->second;
1172 if (targetName != NULL && (job->Target() == NULL
1173 || job->Target()->Title() != targetName)) {
1174 continue;
1176 reply.AddString("job", iterator->first);
1180 reply.what = status;
1181 message->SendReply(&reply);
1185 void
1186 LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message)
1188 uid_t user = _GetUserID(message);
1189 if (user < 0)
1190 return;
1192 const char* name = message->GetString("name");
1193 Job* job = FindJob(name);
1194 if (job == NULL && !fUserMode) {
1195 _ForwardEventMessage(user, message);
1196 return;
1199 BMessage info(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND));
1200 if (job != NULL) {
1201 _GetBaseJobInfo(job, info);
1203 info.SetInt32("team", job->Team());
1204 info.SetBool("enabled", job->IsEnabled());
1205 info.SetBool("running", job->IsRunning());
1206 info.SetBool("launched", job->IsLaunched());
1207 info.SetBool("service", job->IsService());
1209 if (job->Target() != NULL)
1210 info.SetString("target", job->Target()->Name());
1212 for (int32 i = 0; i < job->Arguments().CountStrings(); i++)
1213 info.AddString("launch", job->Arguments().StringAt(i));
1215 for (int32 i = 0; i < job->Requirements().CountStrings(); i++)
1216 info.AddString("requires", job->Requirements().StringAt(i));
1218 PortMap::const_iterator iterator = job->Ports().begin();
1219 for (; iterator != job->Ports().end(); iterator++)
1220 info.AddMessage("port", &iterator->second);
1222 message->SendReply(&info);
1226 uid_t
1227 LaunchDaemon::_GetUserID(BMessage* message)
1229 uid_t user = (uid_t)message->GetInt32("user", -1);
1230 if (user < 0) {
1231 BMessage reply((uint32)B_BAD_VALUE);
1232 message->SendReply(&reply);
1234 return user;
1238 void
1239 LaunchDaemon::_ReadPaths(const BStringList& paths)
1241 for (int32 i = 0; i < paths.CountStrings(); i++) {
1242 BEntry entry(paths.StringAt(i));
1243 if (entry.InitCheck() != B_OK || !entry.Exists())
1244 continue;
1246 _ReadDirectory(NULL, entry);
1251 void
1252 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry)
1254 if (entry.IsDirectory())
1255 _ReadDirectory(context, entry);
1256 else
1257 _ReadFile(context, entry);
1261 void
1262 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry)
1264 BDirectory directory(&directoryEntry);
1266 BEntry entry;
1267 while (directory.GetNextEntry(&entry) == B_OK) {
1268 _ReadEntry(context, entry);
1273 status_t
1274 LaunchDaemon::_ReadFile(const char* context, BEntry& entry)
1276 BPath path;
1277 status_t status = path.SetTo(&entry);
1278 if (status != B_OK)
1279 return status;
1281 SettingsParser parser;
1282 BMessage message;
1283 status = parser.ParseFile(path.Path(), message);
1284 if (status == B_OK) {
1285 TRACE("launch_daemon: read file %s\n", path.Path());
1286 _AddJobs(NULL, message);
1287 _AddTargets(message);
1288 _AddRunTargets(message);
1291 return status;
1295 void
1296 LaunchDaemon::_AddJobs(Target* target, BMessage& message)
1298 BMessage job;
1299 for (int32 index = 0; message.FindMessage("service", index,
1300 &job) == B_OK; index++) {
1301 _AddJob(target, true, job);
1304 for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK;
1305 index++) {
1306 _AddJob(target, false, job);
1311 void
1312 LaunchDaemon::_AddTargets(BMessage& message)
1314 BMessage targetMessage;
1315 for (int32 index = 0; message.FindMessage("target", index,
1316 &targetMessage) == B_OK; index++) {
1317 const char* name = targetMessage.GetString("name");
1318 if (name == NULL) {
1319 // TODO: log error
1320 debug_printf("Target has no name, ignoring it!\n");
1321 continue;
1324 Target* target = FindTarget(name);
1325 if (target == NULL) {
1326 target = new Target(name);
1327 _AddTarget(target);
1328 } else if (targetMessage.GetBool("reset")) {
1329 // Remove all jobs from this target
1330 for (JobMap::iterator iterator = fJobs.begin();
1331 iterator != fJobs.end();) {
1332 Job* job = iterator->second;
1333 JobMap::iterator remove = iterator++;
1335 if (job->Target() == target) {
1336 fJobs.erase(remove);
1337 delete job;
1342 _SetCondition(target, targetMessage);
1343 _SetEvent(target, targetMessage);
1344 _SetEnvironment(target, targetMessage);
1345 _AddJobs(target, targetMessage);
1347 if (target->Event() != NULL)
1348 target->Event()->Register(*this);
1353 void
1354 LaunchDaemon::_AddRunTargets(BMessage& message)
1356 BMessage runMessage;
1357 for (int32 index = 0; message.FindMessage("run", index,
1358 &runMessage) == B_OK; index++) {
1359 BMessage conditions;
1360 bool pass = true;
1361 if (runMessage.FindMessage("if", &conditions) == B_OK) {
1362 Condition* condition = Conditions::FromMessage(conditions);
1363 if (condition != NULL) {
1364 pass = condition->Test(*this);
1365 debug_printf("Test: %s -> %d\n", condition->ToString().String(),
1366 pass);
1367 delete condition;
1368 } else
1369 debug_printf("Could not parse condition!\n");
1372 if (pass) {
1373 _AddRunTargets(runMessage, NULL);
1374 _AddRunTargets(runMessage, "then");
1375 } else {
1376 _AddRunTargets(runMessage, "else");
1382 void
1383 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name)
1385 BMessage targets;
1386 if (name != NULL && message.FindMessage(name, &targets) != B_OK)
1387 return;
1389 const char* target;
1390 for (int32 index = 0; targets.FindString("target", index, &target) == B_OK;
1391 index++) {
1392 fRunTargets.Add(target);
1397 void
1398 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message)
1400 BString name = message.GetString("name");
1401 if (name.IsEmpty()) {
1402 // Invalid job description
1403 return;
1405 name.ToLower();
1407 Job* job = FindJob(name);
1408 if (job == NULL) {
1409 TRACE(" add job \"%s\"\n", name.String());
1411 job = new (std::nothrow) Job(name);
1412 if (job == NULL)
1413 return;
1415 job->SetTeamRegistrator(this);
1416 job->SetService(service);
1417 job->SetCreateDefaultPort(service);
1418 job->SetTarget(target);
1419 } else
1420 TRACE(" amend job \"%s\"\n", name.String());
1422 if (message.HasBool("disabled"))
1423 job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled()));
1425 if (message.HasBool("legacy"))
1426 job->SetCreateDefaultPort(!message.GetBool("legacy", !service));
1428 _SetCondition(job, message);
1429 _SetEvent(job, message);
1430 _SetEnvironment(job, message);
1432 BMessage portMessage;
1433 for (int32 index = 0;
1434 message.FindMessage("port", index, &portMessage) == B_OK; index++) {
1435 job->AddPort(portMessage);
1438 if (message.HasString("launch"))
1439 message.FindStrings("launch", &job->Arguments());
1441 const char* requirement;
1442 for (int32 index = 0;
1443 message.FindString("requires", index, &requirement) == B_OK;
1444 index++) {
1445 job->AddRequirement(requirement);
1447 if (fInitTarget != NULL)
1448 job->AddRequirement(fInitTarget->Name());
1450 fJobs.insert(std::make_pair(job->Title(), job));
1454 /*! Initializes all jobs for the specified target (may be \c NULL).
1455 Jobs that cannot be initialized, and those that never will be due to
1456 conditions, will be removed from the list.
1458 void
1459 LaunchDaemon::_InitJobs(Target* target)
1461 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) {
1462 Job* job = iterator->second;
1463 JobMap::iterator remove = iterator++;
1465 if (job->Target() != target)
1466 continue;
1468 status_t status = B_NO_INIT;
1469 if (job->IsEnabled()) {
1470 // Filter out jobs that have a constant and failing condition
1471 if (job->Condition() == NULL || !job->Condition()->IsConstant(*this)
1472 || job->Condition()->Test(*this)) {
1473 std::set<BString> dependencies;
1474 status = job->Init(*this, dependencies);
1475 if (status == B_OK && job->Event() != NULL)
1476 status = job->Event()->Register(*this);
1480 if (status != B_OK) {
1481 if (status != B_NO_INIT) {
1482 // TODO: log error
1483 debug_printf("Init \"%s\" failed: %s\n", job->Name(),
1484 strerror(status));
1487 // Remove jobs that won't be used later on
1488 fJobs.erase(remove);
1489 delete job;
1495 /*! Adds all jobs for the specified target (may be \c NULL) to the launch
1496 queue, except those that are triggered by events that haven't been
1497 triggered yet.
1499 Unless \a forceNow is true, the target is only launched if its events,
1500 if any, have been triggered already, and its conditions are met.
1502 void
1503 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow)
1505 if (!forceNow && target != NULL && (!target->EventHasTriggered()
1506 || !target->CheckCondition(*this))) {
1507 return;
1510 if (target != NULL && !target->HasLaunched()) {
1511 target->SetLaunched(true);
1512 _InitJobs(target);
1515 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1516 iterator++) {
1517 Job* job = iterator->second;
1518 if (job->Target() == target)
1519 _LaunchJob(job);
1524 /*! Stops all running jobs of the specified target (may be \c NULL).
1526 void
1527 LaunchDaemon::_StopJobs(Target* target, bool force)
1529 if (target != NULL && !target->HasLaunched())
1530 return;
1532 for (JobMap::reverse_iterator iterator = fJobs.rbegin();
1533 iterator != fJobs.rend(); iterator++) {
1534 Job* job = iterator->second;
1535 if (job->Target() == target)
1536 _StopJob(job, force);
1541 /*! Checks whether or not the specified \a job can be launched.
1542 If \a testOnly is \c false, calling this method will trigger a demand
1543 to the \a job.
1545 bool
1546 LaunchDaemon::_CanLaunchJob(Job* job, uint32 options, bool testOnly)
1548 if (job == NULL || !job->CanBeLaunched())
1549 return false;
1551 return (options & FORCE_NOW) != 0
1552 || (job->EventHasTriggered() && job->CheckCondition(*this)
1553 && ((options & TRIGGER_DEMAND) == 0
1554 || Events::TriggerDemand(job->Event(), testOnly)));
1558 /*! Checks recursively if the requirements of the specified job can be launched,
1559 if they are not running already.
1560 Calling this method will not trigger a demand for the requirements.
1562 bool
1563 LaunchDaemon::_CanLaunchJobRequirements(Job* job, uint32 options)
1565 int32 count = job->Requirements().CountStrings();
1566 for (int32 index = 0; index < count; index++) {
1567 Job* requirement = FindJob(job->Requirements().StringAt(index));
1568 if (requirement != NULL
1569 && !requirement->IsRunning() && !requirement->IsLaunching()
1570 && (!_CanLaunchJob(requirement, options, true)
1571 || _CanLaunchJobRequirements(requirement, options))) {
1572 requirement->AddPending(job->Name());
1573 return false;
1577 return true;
1581 /*! Adds the specified \a job to the launch queue
1582 queue, except those that are triggered by events.
1584 Unless \c FORCE_NOW is set, the target is only launched if its events,
1585 if any, have been triggered already.
1587 Calling this method will trigger a demand event if \c TRIGGER_DEMAND has
1588 been set.
1590 bool
1591 LaunchDaemon::_LaunchJob(Job* job, uint32 options)
1593 if (job != NULL && (job->IsLaunching() || job->IsRunning()))
1594 return true;
1596 if (!_CanLaunchJob(job, options))
1597 return false;
1599 // Test if we can launch all requirements
1600 if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND))
1601 return false;
1603 // Actually launch the requirements
1604 int32 count = job->Requirements().CountStrings();
1605 for (int32 index = 0; index < count; index++) {
1606 Job* requirement = FindJob(job->Requirements().StringAt(index));
1607 if (requirement != NULL) {
1608 // TODO: For jobs that have their communication channels set up,
1609 // we would not need to trigger demand at this point
1610 if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) {
1611 // Failed to put a requirement into the launch queue
1612 return false;
1617 if (job->Target() != NULL)
1618 job->Target()->ResolveSourceFiles();
1619 if (job->Event() != NULL)
1620 job->Event()->ResetTrigger();
1622 job->SetLaunching(true);
1624 status_t status = fJobQueue.AddJob(job);
1625 if (status != B_OK) {
1626 debug_printf("Adding job %s to queue failed: %s\n", job->Name(),
1627 strerror(status));
1628 return false;
1631 // Try to launch pending jobs as well
1632 count = job->Pending().CountStrings();
1633 for (int32 index = 0; index < count; index++) {
1634 Job* pending = FindJob(job->Pending().StringAt(index));
1635 if (pending != NULL && _LaunchJob(pending, 0)) {
1636 // Remove the job from the pending list once its in the launch
1637 // queue, so that is not being launched again next time.
1638 index--;
1639 count--;
1643 return true;
1647 void
1648 LaunchDaemon::_StopJob(Job* job, bool force)
1650 // TODO: find out which jobs require this job, and don't stop if any,
1651 // unless force, and then stop them all.
1652 job->SetEnabled(false);
1654 if (!job->IsRunning())
1655 return;
1657 // Be nice first, and send a simple quit message
1658 BMessenger messenger;
1659 if (job->GetMessenger(messenger) == B_OK) {
1660 BMessage request(B_QUIT_REQUESTED);
1661 messenger.SendMessage(&request);
1663 // TODO: wait a bit before going further
1664 return;
1666 // TODO: allow custom shutdown
1668 send_signal(-job->Team(), SIGINT);
1669 // TODO: this would be the next step, again, after a delay
1670 //send_signal(job->Team(), SIGKILL);
1674 void
1675 LaunchDaemon::_AddTarget(Target* target)
1677 fTargets.insert(std::make_pair(target->Title(), target));
1681 void
1682 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message)
1684 Condition* condition = job->Condition();
1685 bool updated = false;
1687 BMessage conditions;
1688 if (message.FindMessage("if", &conditions) == B_OK) {
1689 condition = Conditions::FromMessage(conditions);
1690 updated = true;
1693 if (message.GetBool("no_safemode")) {
1694 condition = Conditions::AddNotSafeMode(condition);
1695 updated = true;
1698 if (updated)
1699 job->SetCondition(condition);
1703 void
1704 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message)
1706 Event* event = job->Event();
1707 bool updated = false;
1709 BMessage events;
1710 if (message.FindMessage("on", &events) == B_OK) {
1711 event = Events::FromMessage(this, events);
1712 updated = true;
1715 if (message.GetBool("on_demand")) {
1716 event = Events::AddOnDemand(this, event);
1717 updated = true;
1720 if (updated) {
1721 TRACE(" event: %s\n", event->ToString().String());
1722 job->SetEvent(event);
1723 _ResolveExternalEvents(job);
1728 void
1729 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message)
1731 BMessage environmentMessage;
1732 if (message.FindMessage("env", &environmentMessage) == B_OK)
1733 job->SetEnvironment(environmentMessage);
1737 ExternalEventSource*
1738 LaunchDaemon::_FindEvent(const char* owner, const char* name) const
1740 if (name == NULL)
1741 return NULL;
1743 BString eventName = name;
1744 eventName.ToLower();
1746 EventMap::const_iterator found = fEvents.find(eventName);
1747 if (found != fEvents.end())
1748 return found->second;
1750 if (owner == NULL)
1751 return NULL;
1753 eventName.Prepend("/");
1754 eventName.Prepend(get_leaf(owner));
1756 found = fEvents.find(eventName);
1757 if (found != fEvents.end())
1758 return found->second;
1760 return NULL;
1764 void
1765 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event,
1766 const BString& name)
1768 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();
1769 iterator++) {
1770 Job* job = iterator->second;
1771 if (Events::ResolveExternalEvent(job->Event(), name, event->Flags()))
1772 event->AddListener(job);
1777 void
1778 LaunchDaemon::_ResolveExternalEvents(BaseJob* job)
1780 if (job->Event() == NULL)
1781 return;
1783 for (EventMap::iterator iterator = fEvents.begin();
1784 iterator != fEvents.end(); iterator++) {
1785 ExternalEventSource* event = iterator->second;
1786 if (Events::ResolveExternalEvent(job->Event(), event->Name(),
1787 event->Flags()))
1788 event->AddListener(job);
1793 void
1794 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info)
1796 info.SetString("name", job->Name());
1798 if (job->Event() != NULL)
1799 info.SetString("event", job->Event()->ToString());
1801 if (job->Condition() != NULL)
1802 info.SetString("condition", job->Condition()->ToString());
1806 void
1807 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message)
1809 if (fUserMode)
1810 return;
1812 // Forward event to user launch_daemon(s)
1813 if (user == 0) {
1814 for (SessionMap::iterator iterator = fSessions.begin();
1815 iterator != fSessions.end(); iterator++) {
1816 Session* session = iterator->second;
1817 session->Daemon().SendMessage(message);
1818 // ignore reply
1820 } else {
1821 Session* session = FindSession(user);
1822 if (session != NULL)
1823 session->Daemon().SendMessage(message);
1828 status_t
1829 LaunchDaemon::_StartSession(const char* login)
1831 // TODO: enable user/group code
1832 // The launch_daemon currently cannot talk to the registrar, though
1834 struct passwd* passwd = getpwnam(login);
1835 if (passwd == NULL)
1836 return B_NAME_NOT_FOUND;
1837 if (strcmp(passwd->pw_name, login) != 0)
1838 return B_NAME_NOT_FOUND;
1840 // Check if there is a user session running already
1841 uid_t user = passwd->pw_uid;
1842 gid_t group = passwd->pw_gid;
1844 Unlock();
1846 if (fork() == 0) {
1847 if (setsid() < 0)
1848 exit(EXIT_FAILURE);
1850 if (initgroups(login, group) == -1)
1851 exit(EXIT_FAILURE);
1852 if (setgid(group) != 0)
1853 exit(EXIT_FAILURE);
1854 if (setuid(user) != 0)
1855 exit(EXIT_FAILURE);
1857 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') {
1858 setenv("HOME", passwd->pw_dir, true);
1860 if (chdir(passwd->pw_dir) != 0) {
1861 debug_printf("Could not switch to home dir %s: %s\n",
1862 passwd->pw_dir, strerror(errno));
1866 // TODO: This leaks the parent application
1867 be_app = NULL;
1869 // Reinitialize be_roster
1870 BRoster::Private().DeleteBeRoster();
1871 BRoster::Private().InitBeRoster();
1873 // TODO: take over system jobs, and reserve their names (or ask parent)
1874 status_t status;
1875 LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status);
1876 if (status == B_OK)
1877 daemon->Run();
1879 delete daemon;
1880 exit(EXIT_SUCCESS);
1882 Lock();
1883 return B_OK;
1887 void
1888 LaunchDaemon::_RetrieveKernelOptions()
1890 char buffer[32];
1891 size_t size = sizeof(buffer);
1892 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer,
1893 &size);
1894 if (status == B_OK) {
1895 fSafeMode = !strncasecmp(buffer, "true", size)
1896 || !strncasecmp(buffer, "yes", size)
1897 || !strncasecmp(buffer, "on", size)
1898 || !strncasecmp(buffer, "enabled", size);
1899 } else
1900 fSafeMode = false;
1904 void
1905 LaunchDaemon::_SetupEnvironment()
1907 // Determine safemode kernel option
1908 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true);
1910 // Default locale settings
1911 setenv("LC_TYPE", "en_US.UTF-8", true);
1915 /*! Basic system initialization that must happen before any jobs are launched.
1917 void
1918 LaunchDaemon::_InitSystem()
1920 #ifndef TEST_MODE
1921 _AddInitJob(new InitRealTimeClockJob());
1922 _AddInitJob(new InitSharedMemoryDirectoryJob());
1923 _AddInitJob(new InitTemporaryDirectoryJob());
1924 #endif
1926 fJobQueue.AddJob(fInitTarget);
1930 void
1931 LaunchDaemon::_AddInitJob(BJob* job)
1933 fInitTarget->AddDependency(job);
1934 fJobQueue.AddJob(job);
1938 // #pragma mark -
1941 #ifndef TEST_MODE
1944 static void
1945 open_stdio(int targetFD, int openMode)
1947 #ifdef DEBUG
1948 int fd = open("/dev/dprintf", openMode);
1949 #else
1950 int fd = open("/dev/null", openMode);
1951 #endif
1952 if (fd != targetFD) {
1953 dup2(fd, targetFD);
1954 close(fd);
1959 #endif // TEST_MODE
1963 main()
1965 if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) {
1966 fprintf(stderr, "The launch_daemon is already running!\n");
1967 return EXIT_FAILURE;
1970 #ifndef TEST_MODE
1971 // Make stdin/out/err available
1972 open_stdio(STDIN_FILENO, O_RDONLY);
1973 open_stdio(STDOUT_FILENO, O_WRONLY);
1974 dup2(STDOUT_FILENO, STDERR_FILENO);
1975 #endif
1977 EventMap events;
1978 status_t status;
1979 LaunchDaemon* daemon = new LaunchDaemon(false, events, status);
1980 if (status == B_OK)
1981 daemon->Run();
1983 delete daemon;
1984 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE;