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", ¶meter);
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