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