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 82 uid_t User() const 83 { return fUser; } 84 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 107 const BMessenger& Source() const 108 { return fSource; } 109 110 const char* Name() const; 111 const char* OwnerName() const; 112 uint32 Flags() const 113 { return fFlags; } 114 115 void Trigger(); 116 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* 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 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 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 294 ExternalEventSource::~ExternalEventSource() 295 { 296 } 297 298 299 const char* 300 ExternalEventSource::Name() const 301 { 302 return fName.String(); 303 } 304 305 306 const char* 307 ExternalEventSource::OwnerName() const 308 { 309 return fOwnerName.String(); 310 } 311 312 313 void 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 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 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 349 ExternalEventSource::RemoveDestination(Event* event) 350 { 351 fDestinations.RemoveItem(event); 352 } 353 354 355 // #pragma mark - 356 357 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 384 LaunchDaemon::~LaunchDaemon() 385 { 386 } 387 388 389 Job* 390 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* 404 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* 418 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 429 LaunchDaemon::IsSafeMode() const 430 { 431 return fSafeMode; 432 } 433 434 435 bool 436 LaunchDaemon::BootVolumeIsReadOnly() const 437 { 438 return fReadOnlyBootVolume; 439 } 440 441 442 status_t 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1861 LaunchDaemon::_AddTarget(Target* target) 1862 { 1863 fTargets.insert(std::make_pair(target->Title(), target)); 1864 } 1865 1866 1867 void 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 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 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* 1923 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 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 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 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 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 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 2034 LaunchDaemon::_SetupEnvironment() 2035 { 2036 // Determine safemode kernel option 2037 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true); 2038 2039 // Default locale settings 2040 setenv("LC_TYPE", "en_US.UTF-8", true); 2041 } 2042 2043 2044 /*! Basic system initialization that must happen before any jobs are launched. 2045 */ 2046 void 2047 LaunchDaemon::_InitSystem() 2048 { 2049 #ifndef TEST_MODE 2050 _AddInitJob(new InitRealTimeClockJob()); 2051 _AddInitJob(new InitSharedMemoryDirectoryJob()); 2052 _AddInitJob(new InitTemporaryDirectoryJob()); 2053 #endif 2054 2055 fJobQueue.AddJob(fInitTarget); 2056 } 2057 2058 2059 void 2060 LaunchDaemon::_AddInitJob(BJob* job) 2061 { 2062 fInitTarget->AddDependency(job); 2063 fJobQueue.AddJob(job); 2064 } 2065 2066 2067 // #pragma mark - 2068 2069 2070 #ifndef TEST_MODE 2071 2072 2073 static void 2074 open_stdio(int targetFD, int openMode) 2075 { 2076 #ifdef DEBUG 2077 int fd = open("/dev/dprintf", openMode); 2078 #else 2079 int fd = open("/dev/null", openMode); 2080 #endif 2081 if (fd != targetFD) { 2082 dup2(fd, targetFD); 2083 close(fd); 2084 } 2085 } 2086 2087 2088 #endif // TEST_MODE 2089 2090 2091 static int 2092 user_main(const char* login) 2093 { 2094 struct passwd* passwd = getpwnam(login); 2095 if (passwd == NULL) 2096 return B_NAME_NOT_FOUND; 2097 if (strcmp(passwd->pw_name, login) != 0) 2098 return B_NAME_NOT_FOUND; 2099 2100 // Check if there is a user session running already 2101 uid_t user = passwd->pw_uid; 2102 gid_t group = passwd->pw_gid; 2103 2104 if (setsid() < 0) 2105 exit(EXIT_FAILURE); 2106 2107 if (initgroups(login, group) == -1) 2108 exit(EXIT_FAILURE); 2109 if (setgid(group) != 0) 2110 exit(EXIT_FAILURE); 2111 if (setuid(user) != 0) 2112 exit(EXIT_FAILURE); 2113 2114 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') { 2115 setenv("HOME", passwd->pw_dir, true); 2116 2117 if (chdir(passwd->pw_dir) != 0) { 2118 debug_printf("Could not switch to home dir %s: %s\n", 2119 passwd->pw_dir, strerror(errno)); 2120 } 2121 } 2122 2123 // TODO: take over system jobs, and reserve their names? (by asking parent) 2124 status_t status; 2125 LaunchDaemon* daemon = new LaunchDaemon(true, status); 2126 if (status == B_OK) 2127 daemon->Run(); 2128 2129 delete daemon; 2130 return 0; 2131 } 2132 2133 2134 int 2135 main(int argc, char* argv[]) 2136 { 2137 if (argc == 2 && geteuid() == 0) 2138 return user_main(argv[1]); 2139 2140 if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) { 2141 fprintf(stderr, "The launch_daemon is already running!\n"); 2142 return EXIT_FAILURE; 2143 } 2144 2145 #ifndef TEST_MODE 2146 // Make stdin/out/err available 2147 open_stdio(STDIN_FILENO, O_RDONLY); 2148 open_stdio(STDOUT_FILENO, O_WRONLY); 2149 dup2(STDOUT_FILENO, STDERR_FILENO); 2150 #endif 2151 2152 status_t status; 2153 LaunchDaemon* daemon = new LaunchDaemon(false, status); 2154 if (status == B_OK) 2155 daemon->Run(); 2156 2157 delete daemon; 2158 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 2159 } 2160