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 bool inProgress = false; 531 BRoster roster; 532 BRoster::Private rosterPrivate(roster); 533 status_t status = rosterPrivate.IsShutDownInProgress( 534 &inProgress); 535 if (status != B_OK || !inProgress) { 536 // TODO: take restart throttle into account 537 _LaunchJob(job); 538 } 539 } 540 } 541 break; 542 } 543 case B_SOME_APP_LAUNCHED: 544 { 545 team_id team = (team_id)message->GetInt32("be:team", -1); 546 Job* job = NULL; 547 548 MutexLocker locker(fTeamsLock); 549 550 TeamMap::iterator found = fTeams.find(team); 551 if (found != fTeams.end()) { 552 job = found->second; 553 locker.Unlock(); 554 } else { 555 locker.Unlock(); 556 557 // Find job by name instead 558 const char* signature = message->GetString("be:signature"); 559 job = FindJob(get_leaf(signature)); 560 if (job != NULL) { 561 TRACE("Updated default port of untracked team %d, %s\n", 562 (int)team, signature); 563 } 564 } 565 566 if (job != NULL) { 567 // Update port info 568 app_info info; 569 status_t status = be_roster->GetRunningAppInfo(team, &info); 570 if (status == B_OK && info.port != job->DefaultPort()) { 571 TRACE("Update default port for %s to %d\n", job->Name(), 572 (int)info.port); 573 job->SetDefaultPort(info.port); 574 } 575 } 576 break; 577 } 578 579 case B_GET_LAUNCH_DATA: 580 _HandleGetLaunchData(message); 581 break; 582 583 case B_LAUNCH_TARGET: 584 _HandleLaunchTarget(message); 585 break; 586 case B_STOP_LAUNCH_TARGET: 587 _HandleStopLaunchTarget(message); 588 break; 589 case B_LAUNCH_JOB: 590 _HandleLaunchJob(message); 591 break; 592 case B_ENABLE_LAUNCH_JOB: 593 _HandleEnableLaunchJob(message); 594 break; 595 case B_STOP_LAUNCH_JOB: 596 _HandleStopLaunchJob(message); 597 break; 598 599 case B_LAUNCH_SESSION: 600 _HandleLaunchSession(message); 601 break; 602 case B_REGISTER_SESSION_DAEMON: 603 _HandleRegisterSessionDaemon(message); 604 break; 605 606 case B_REGISTER_LAUNCH_EVENT: 607 _HandleRegisterLaunchEvent(message); 608 break; 609 case B_UNREGISTER_LAUNCH_EVENT: 610 _HandleUnregisterLaunchEvent(message); 611 break; 612 case B_NOTIFY_LAUNCH_EVENT: 613 _HandleNotifyLaunchEvent(message); 614 break; 615 case B_RESET_STICKY_LAUNCH_EVENT: 616 _HandleResetStickyLaunchEvent(message); 617 break; 618 619 case B_GET_LAUNCH_TARGETS: 620 _HandleGetLaunchTargets(message); 621 break; 622 case B_GET_LAUNCH_TARGET_INFO: 623 _HandleGetLaunchTargetInfo(message); 624 break; 625 case B_GET_LAUNCH_JOBS: 626 _HandleGetLaunchJobs(message); 627 break; 628 case B_GET_LAUNCH_JOB_INFO: 629 _HandleGetLaunchJobInfo(message); 630 break; 631 632 case B_GET_LAUNCH_LOG: 633 _HandleGetLaunchLog(message); 634 break; 635 636 case kMsgEventTriggered: 637 { 638 // An internal event has been triggered. 639 // Check if its job(s) can be launched now. 640 const char* name = message->GetString("owner"); 641 if (name == NULL) 642 break; 643 644 Event* event = (Event*)message->GetPointer("event"); 645 646 Job* job = FindJob(name); 647 if (job != NULL) { 648 fLog.EventTriggered(job, event); 649 _LaunchJob(job); 650 break; 651 } 652 653 Target* target = FindTarget(name); 654 if (target != NULL) { 655 fLog.EventTriggered(target, event); 656 _LaunchJobs(target); 657 break; 658 } 659 break; 660 } 661 662 default: 663 BServer::MessageReceived(message); 664 break; 665 } 666 } 667 668 669 void 670 LaunchDaemon::_HandleGetLaunchData(BMessage* message) 671 { 672 uid_t user = _GetUserID(message); 673 if (user < 0) 674 return; 675 676 BMessage reply((uint32)B_OK); 677 bool launchJob = true; 678 679 Job* job = FindJob(get_leaf(message->GetString("name"))); 680 if (job == NULL) { 681 Session* session = FindSession(user); 682 if (session != NULL) { 683 // Forward request to user launch_daemon 684 if (session->Daemon().SendMessage(message) == B_OK) 685 return; 686 } 687 reply.what = B_NAME_NOT_FOUND; 688 } else if (job->IsService() && !job->IsLaunched()) { 689 if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) { 690 // The job exists, but cannot be started yet, as its 691 // conditions are not met; don't make it available yet 692 // TODO: we may not want to initialize jobs with conditions 693 // that aren't met yet 694 reply.what = B_NO_INIT; 695 } else if (job->Event() != NULL) { 696 if (!Events::TriggerDemand(job->Event())) { 697 // The job is not triggered by demand; we cannot start it now 698 reply.what = B_NO_INIT; 699 } else { 700 // The job has already been triggered, don't launch it again 701 launchJob = false; 702 } 703 } 704 } else 705 launchJob = false; 706 707 bool ownsMessage = false; 708 if (reply.what == B_OK) { 709 // Launch the job if it hasn't been launched already 710 if (launchJob) 711 _LaunchJob(job, TRIGGER_DEMAND); 712 713 DetachCurrentMessage(); 714 status_t result = job->HandleGetLaunchData(message); 715 if (result == B_OK) { 716 // Replying is delegated to the job. 717 return; 718 } 719 720 ownsMessage = true; 721 reply.what = result; 722 } 723 724 message->SendReply(&reply); 725 if (ownsMessage) 726 delete message; 727 } 728 729 730 void 731 LaunchDaemon::_HandleLaunchTarget(BMessage* message) 732 { 733 uid_t user = _GetUserID(message); 734 if (user < 0) 735 return; 736 737 const char* name = message->GetString("target"); 738 const char* baseName = message->GetString("base target"); 739 740 Target* target = FindTarget(name); 741 if (target == NULL && baseName != NULL) { 742 Target* baseTarget = FindTarget(baseName); 743 if (baseTarget != NULL) { 744 target = new Target(name); 745 746 // Copy all jobs with the base target into the new target 747 for (JobMap::iterator iterator = fJobs.begin(); 748 iterator != fJobs.end();) { 749 Job* job = iterator->second; 750 iterator++; 751 752 if (job->Target() == baseTarget) { 753 Job* copy = new Job(*job); 754 copy->SetTarget(target); 755 756 fJobs.insert(std::make_pair(copy->Name(), copy)); 757 } 758 } 759 } 760 } 761 if (target == NULL) { 762 Session* session = FindSession(user); 763 if (session != NULL) { 764 // Forward request to user launch_daemon 765 if (session->Daemon().SendMessage(message) == B_OK) 766 return; 767 } 768 769 BMessage reply(B_NAME_NOT_FOUND); 770 message->SendReply(&reply); 771 return; 772 } 773 774 BMessage data; 775 if (message->FindMessage("data", &data) == B_OK) 776 target->AddData(data.GetString("name"), data); 777 778 _LaunchJobs(target); 779 780 BMessage reply((uint32)B_OK); 781 message->SendReply(&reply); 782 } 783 784 785 void 786 LaunchDaemon::_HandleStopLaunchTarget(BMessage* message) 787 { 788 uid_t user = _GetUserID(message); 789 if (user < 0) 790 return; 791 792 const char* name = message->GetString("target"); 793 794 Target* target = FindTarget(name); 795 if (target == NULL) { 796 Session* session = FindSession(user); 797 if (session != NULL) { 798 // Forward request to user launch_daemon 799 if (session->Daemon().SendMessage(message) == B_OK) 800 return; 801 } 802 803 BMessage reply(B_NAME_NOT_FOUND); 804 message->SendReply(&reply); 805 return; 806 } 807 808 BMessage data; 809 if (message->FindMessage("data", &data) == B_OK) 810 target->AddData(data.GetString("name"), data); 811 812 bool force = message->GetBool("force"); 813 fLog.JobStopped(target, force); 814 _StopJobs(target, force); 815 816 BMessage reply((uint32)B_OK); 817 message->SendReply(&reply); 818 } 819 820 821 void 822 LaunchDaemon::_HandleLaunchJob(BMessage* message) 823 { 824 uid_t user = _GetUserID(message); 825 if (user < 0) 826 return; 827 828 const char* name = message->GetString("name"); 829 830 Job* job = FindJob(name); 831 if (job == NULL) { 832 Session* session = FindSession(user); 833 if (session != NULL) { 834 // Forward request to user launch_daemon 835 if (session->Daemon().SendMessage(message) == B_OK) 836 return; 837 } 838 839 BMessage reply(B_NAME_NOT_FOUND); 840 message->SendReply(&reply); 841 return; 842 } 843 844 job->SetEnabled(true); 845 _LaunchJob(job, FORCE_NOW); 846 847 BMessage reply((uint32)B_OK); 848 message->SendReply(&reply); 849 } 850 851 852 void 853 LaunchDaemon::_HandleEnableLaunchJob(BMessage* message) 854 { 855 uid_t user = _GetUserID(message); 856 if (user < 0) 857 return; 858 859 const char* name = message->GetString("name"); 860 bool enable = message->GetBool("enable"); 861 862 Job* job = FindJob(name); 863 if (job == NULL) { 864 Session* session = FindSession(user); 865 if (session != NULL) { 866 // Forward request to user launch_daemon 867 if (session->Daemon().SendMessage(message) == B_OK) 868 return; 869 } 870 871 BMessage reply(B_NAME_NOT_FOUND); 872 message->SendReply(&reply); 873 return; 874 } 875 876 job->SetEnabled(enable); 877 fLog.JobEnabled(job, enable); 878 879 BMessage reply((uint32)B_OK); 880 message->SendReply(&reply); 881 } 882 883 884 void 885 LaunchDaemon::_HandleStopLaunchJob(BMessage* message) 886 { 887 uid_t user = _GetUserID(message); 888 if (user < 0) 889 return; 890 891 const char* name = message->GetString("name"); 892 893 Job* job = FindJob(name); 894 if (job == NULL) { 895 Session* session = FindSession(user); 896 if (session != NULL) { 897 // Forward request to user launch_daemon 898 if (session->Daemon().SendMessage(message) == B_OK) 899 return; 900 } 901 902 BMessage reply(B_NAME_NOT_FOUND); 903 message->SendReply(&reply); 904 return; 905 } 906 907 bool force = message->GetBool("force"); 908 fLog.JobStopped(job, force); 909 _StopJob(job, force); 910 911 BMessage reply((uint32)B_OK); 912 message->SendReply(&reply); 913 } 914 915 916 void 917 LaunchDaemon::_HandleLaunchSession(BMessage* message) 918 { 919 uid_t user = _GetUserID(message); 920 if (user < 0) 921 return; 922 923 status_t status = B_OK; 924 const char* login = message->GetString("login"); 925 if (login == NULL) 926 status = B_BAD_VALUE; 927 if (status == B_OK && user != 0) { 928 // Only the root user can start sessions 929 // TODO: we'd actually need to know the uid of the sender 930 status = B_PERMISSION_DENIED; 931 } 932 if (status == B_OK) 933 status = _StartSession(login); 934 935 BMessage reply((uint32)status); 936 message->SendReply(&reply); 937 } 938 939 940 void 941 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message) 942 { 943 uid_t user = _GetUserID(message); 944 if (user < 0) 945 return; 946 947 status_t status = B_OK; 948 949 BMessenger target; 950 if (message->FindMessenger("daemon", &target) != B_OK) 951 status = B_BAD_VALUE; 952 953 if (status == B_OK) { 954 Session* session = new (std::nothrow) Session(user, target); 955 if (session != NULL) 956 fSessions.insert(std::make_pair(user, session)); 957 else 958 status = B_NO_MEMORY; 959 } 960 961 BMessage reply((uint32)status); 962 message->SendReply(&reply); 963 } 964 965 966 void 967 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message) 968 { 969 uid_t user = _GetUserID(message); 970 if (user < 0) 971 return; 972 973 if (user == 0 || fUserMode) { 974 status_t status = B_OK; 975 976 const char* name = message->GetString("name"); 977 const char* ownerName = message->GetString("owner"); 978 uint32 flags = message->GetUInt32("flags", 0); 979 BMessenger source; 980 if (name != NULL && ownerName != NULL 981 && message->FindMessenger("source", &source) == B_OK) { 982 // Register event 983 ownerName = get_leaf(ownerName); 984 985 ExternalEventSource* event = new (std::nothrow) 986 ExternalEventSource(source, ownerName, name, flags); 987 if (event != NULL) { 988 // Use short name, and fully qualified name 989 BString eventName = name; 990 fEvents.insert(std::make_pair(eventName, event)); 991 _ResolveExternalEvents(event, eventName); 992 993 eventName.Prepend("/"); 994 eventName.Prepend(ownerName); 995 fEvents.insert(std::make_pair(eventName, event)); 996 _ResolveExternalEvents(event, eventName); 997 998 fLog.ExternalEventRegistered(name); 999 } else 1000 status = B_NO_MEMORY; 1001 } else 1002 status = B_BAD_VALUE; 1003 1004 BMessage reply((uint32)status); 1005 message->SendReply(&reply); 1006 } 1007 1008 _ForwardEventMessage(user, message); 1009 } 1010 1011 1012 void 1013 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message) 1014 { 1015 uid_t user = _GetUserID(message); 1016 if (user < 0) 1017 return; 1018 1019 if (user == 0 || fUserMode) { 1020 status_t status = B_OK; 1021 1022 const char* name = message->GetString("name"); 1023 const char* ownerName = message->GetString("owner"); 1024 BMessenger source; 1025 if (name != NULL && ownerName != NULL 1026 && message->FindMessenger("source", &source) == B_OK) { 1027 // Unregister short and fully qualified event name 1028 ownerName = get_leaf(ownerName); 1029 1030 BString eventName = name; 1031 fEvents.erase(eventName); 1032 1033 eventName.Prepend("/"); 1034 eventName.Prepend(ownerName); 1035 fEvents.erase(eventName); 1036 1037 fLog.ExternalEventRegistered(name); 1038 } else 1039 status = B_BAD_VALUE; 1040 1041 BMessage reply((uint32)status); 1042 message->SendReply(&reply); 1043 } 1044 1045 _ForwardEventMessage(user, message); 1046 } 1047 1048 1049 void 1050 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message) 1051 { 1052 uid_t user = _GetUserID(message); 1053 if (user < 0) 1054 return; 1055 1056 if (user == 0 || fUserMode) { 1057 // Trigger events 1058 const char* name = message->GetString("name"); 1059 const char* ownerName = message->GetString("owner"); 1060 // TODO: support arguments (as selectors) 1061 1062 ExternalEventSource* event = _FindEvent(ownerName, name); 1063 if (event != NULL) { 1064 fLog.ExternalEventTriggered(name); 1065 1066 // Evaluate all of its jobs 1067 int32 count = event->CountListeners(); 1068 for (int32 index = 0; index < count; index++) { 1069 BaseJob* listener = event->ListenerAt(index); 1070 Events::TriggerExternalEvent(listener->Event(), name); 1071 } 1072 } 1073 } 1074 1075 _ForwardEventMessage(user, message); 1076 } 1077 1078 1079 void 1080 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message) 1081 { 1082 uid_t user = _GetUserID(message); 1083 if (user < 0) 1084 return; 1085 1086 if (user == 0 || fUserMode) { 1087 // Reset sticky events 1088 const char* name = message->GetString("name"); 1089 const char* ownerName = message->GetString("owner"); 1090 // TODO: support arguments (as selectors) 1091 1092 ExternalEventSource* event = _FindEvent(ownerName, name); 1093 if (event != NULL) { 1094 // Evaluate all of its jobs 1095 int32 count = event->CountListeners(); 1096 for (int32 index = 0; index < count; index++) { 1097 BaseJob* listener = event->ListenerAt(index); 1098 Events::ResetStickyExternalEvent(listener->Event(), name); 1099 } 1100 } 1101 } 1102 1103 _ForwardEventMessage(user, message); 1104 } 1105 1106 1107 void 1108 LaunchDaemon::_HandleGetLaunchTargets(BMessage* message) 1109 { 1110 uid_t user = _GetUserID(message); 1111 if (user < 0) 1112 return; 1113 1114 BMessage reply; 1115 status_t status = B_OK; 1116 1117 if (!fUserMode) { 1118 // Request the data from the user's daemon, too 1119 Session* session = FindSession(user); 1120 if (session != NULL) { 1121 BMessage request(B_GET_LAUNCH_TARGETS); 1122 status = request.AddInt32("user", 0); 1123 if (status == B_OK) { 1124 status = session->Daemon().SendMessage(&request, 1125 &reply); 1126 } 1127 if (status == B_OK) 1128 status = reply.what; 1129 } else 1130 status = B_NAME_NOT_FOUND; 1131 } 1132 1133 if (status == B_OK) { 1134 TargetMap::const_iterator iterator = fTargets.begin(); 1135 for (; iterator != fTargets.end(); iterator++) 1136 reply.AddString("target", iterator->first); 1137 } 1138 1139 reply.what = status; 1140 message->SendReply(&reply); 1141 } 1142 1143 1144 void 1145 LaunchDaemon::_HandleGetLaunchTargetInfo(BMessage* message) 1146 { 1147 uid_t user = _GetUserID(message); 1148 if (user < 0) 1149 return; 1150 1151 const char* name = message->GetString("name"); 1152 Target* target = FindTarget(name); 1153 if (target == NULL && !fUserMode) { 1154 _ForwardEventMessage(user, message); 1155 return; 1156 } 1157 1158 BMessage info(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND)); 1159 if (target != NULL) { 1160 _GetBaseJobInfo(target, info); 1161 1162 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1163 iterator++) { 1164 Job* job = iterator->second; 1165 if (job->Target() == target) 1166 info.AddString("job", job->Name()); 1167 } 1168 } 1169 message->SendReply(&info); 1170 } 1171 1172 1173 void 1174 LaunchDaemon::_HandleGetLaunchJobs(BMessage* message) 1175 { 1176 uid_t user = _GetUserID(message); 1177 if (user < 0) 1178 return; 1179 1180 const char* targetName = message->GetString("target"); 1181 1182 BMessage reply; 1183 status_t status = B_OK; 1184 1185 if (!fUserMode) { 1186 // Request the data from the user's daemon, too 1187 Session* session = FindSession(user); 1188 if (session != NULL) { 1189 BMessage request(B_GET_LAUNCH_JOBS); 1190 status = request.AddInt32("user", 0); 1191 if (status == B_OK && targetName != NULL) 1192 status = request.AddString("target", targetName); 1193 if (status == B_OK) { 1194 status = session->Daemon().SendMessage(&request, 1195 &reply); 1196 } 1197 if (status == B_OK) 1198 status = reply.what; 1199 } else 1200 status = B_NAME_NOT_FOUND; 1201 } 1202 1203 if (status == B_OK) { 1204 JobMap::const_iterator iterator = fJobs.begin(); 1205 for (; iterator != fJobs.end(); iterator++) { 1206 Job* job = iterator->second; 1207 if (targetName != NULL && (job->Target() == NULL 1208 || job->Target()->Title() != targetName)) { 1209 continue; 1210 } 1211 reply.AddString("job", iterator->first); 1212 } 1213 } 1214 1215 reply.what = status; 1216 message->SendReply(&reply); 1217 } 1218 1219 1220 void 1221 LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message) 1222 { 1223 uid_t user = _GetUserID(message); 1224 if (user < 0) 1225 return; 1226 1227 const char* name = message->GetString("name"); 1228 Job* job = FindJob(name); 1229 if (job == NULL && !fUserMode) { 1230 _ForwardEventMessage(user, message); 1231 return; 1232 } 1233 1234 BMessage info(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND)); 1235 if (job != NULL) { 1236 _GetBaseJobInfo(job, info); 1237 1238 info.SetInt32("team", job->Team()); 1239 info.SetBool("enabled", job->IsEnabled()); 1240 info.SetBool("running", job->IsRunning()); 1241 info.SetBool("launched", job->IsLaunched()); 1242 info.SetBool("service", job->IsService()); 1243 1244 if (job->Target() != NULL) 1245 info.SetString("target", job->Target()->Name()); 1246 1247 for (int32 i = 0; i < job->Arguments().CountStrings(); i++) 1248 info.AddString("launch", job->Arguments().StringAt(i)); 1249 1250 for (int32 i = 0; i < job->Requirements().CountStrings(); i++) 1251 info.AddString("requires", job->Requirements().StringAt(i)); 1252 1253 PortMap::const_iterator iterator = job->Ports().begin(); 1254 for (; iterator != job->Ports().end(); iterator++) 1255 info.AddMessage("port", &iterator->second); 1256 } 1257 message->SendReply(&info); 1258 } 1259 1260 1261 void 1262 LaunchDaemon::_HandleGetLaunchLog(BMessage* message) 1263 { 1264 uid_t user = _GetUserID(message); 1265 if (user < 0) 1266 return; 1267 1268 BMessage filter; 1269 BString jobName; 1270 const char* event = NULL; 1271 int32 limit = 0; 1272 bool systemOnly = false; 1273 bool userOnly = false; 1274 if (message->FindMessage("filter", &filter) == B_OK) { 1275 limit = filter.GetInt32("limit", 0); 1276 jobName = filter.GetString("job"); 1277 jobName.ToLower(); 1278 event = filter.GetString("event"); 1279 systemOnly = filter.GetBool("systemOnly"); 1280 userOnly = filter.GetBool("userOnly"); 1281 } 1282 1283 BMessage info((uint32)B_OK); 1284 int32 count = 0; 1285 1286 if (user == 0 || !userOnly) { 1287 LogItemList::Iterator iterator = fLog.Iterator(); 1288 while (iterator.HasNext()) { 1289 LogItem* item = iterator.Next(); 1290 if (!item->Matches(jobName.IsEmpty() ? NULL : jobName.String(), 1291 event)) { 1292 continue; 1293 } 1294 1295 BMessage itemMessage; 1296 itemMessage.AddUInt64("when", item->When()); 1297 itemMessage.AddInt32("type", (int32)item->Type()); 1298 itemMessage.AddString("message", item->Message()); 1299 1300 BMessage parameter; 1301 item->GetParameter(parameter); 1302 itemMessage.AddMessage("parameter", ¶meter); 1303 1304 info.AddMessage("item", &itemMessage); 1305 1306 // limit == 0 means no limit 1307 if (++count == limit) 1308 break; 1309 } 1310 } 1311 1312 // Get the list from the user daemon, and merge it into our reply 1313 Session* session = FindSession(user); 1314 if (session != NULL && !systemOnly) { 1315 if (limit != 0) { 1316 // Update limit for user daemon 1317 limit -= count; 1318 if (limit <= 0) { 1319 message->SendReply(&info); 1320 return; 1321 } 1322 } 1323 1324 BMessage reply; 1325 1326 BMessage request(B_GET_LAUNCH_LOG); 1327 status_t status = request.AddInt32("user", 0); 1328 if (status == B_OK && (limit != 0 || !jobName.IsEmpty() 1329 || event != NULL)) { 1330 // Forward filter specification when needed 1331 status = filter.SetInt32("limit", limit); 1332 if (status == B_OK) 1333 status = request.AddMessage("filter", &filter); 1334 } 1335 if (status == B_OK) 1336 status = session->Daemon().SendMessage(&request, &reply); 1337 if (status == B_OK) 1338 info.AddMessage("user", &reply); 1339 } 1340 1341 message->SendReply(&info); 1342 } 1343 1344 1345 uid_t 1346 LaunchDaemon::_GetUserID(BMessage* message) 1347 { 1348 uid_t user = (uid_t)message->GetInt32("user", -1); 1349 if (user < 0) { 1350 BMessage reply((uint32)B_BAD_VALUE); 1351 message->SendReply(&reply); 1352 } 1353 return user; 1354 } 1355 1356 1357 void 1358 LaunchDaemon::_ReadPaths(const BStringList& paths) 1359 { 1360 for (int32 i = 0; i < paths.CountStrings(); i++) { 1361 BEntry entry(paths.StringAt(i)); 1362 if (entry.InitCheck() != B_OK || !entry.Exists()) 1363 continue; 1364 1365 _ReadDirectory(NULL, entry); 1366 } 1367 } 1368 1369 1370 void 1371 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry) 1372 { 1373 if (entry.IsDirectory()) 1374 _ReadDirectory(context, entry); 1375 else 1376 _ReadFile(context, entry); 1377 } 1378 1379 1380 void 1381 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry) 1382 { 1383 BDirectory directory(&directoryEntry); 1384 1385 BEntry entry; 1386 while (directory.GetNextEntry(&entry) == B_OK) { 1387 _ReadEntry(context, entry); 1388 } 1389 } 1390 1391 1392 status_t 1393 LaunchDaemon::_ReadFile(const char* context, BEntry& entry) 1394 { 1395 BPath path; 1396 status_t status = path.SetTo(&entry); 1397 if (status != B_OK) 1398 return status; 1399 1400 SettingsParser parser; 1401 BMessage message; 1402 status = parser.ParseFile(path.Path(), message); 1403 if (status == B_OK) { 1404 TRACE("launch_daemon: read file %s\n", path.Path()); 1405 _AddJobs(NULL, message); 1406 _AddTargets(message); 1407 _AddRunTargets(message); 1408 } 1409 1410 return status; 1411 } 1412 1413 1414 void 1415 LaunchDaemon::_AddJobs(Target* target, BMessage& message) 1416 { 1417 BMessage job; 1418 for (int32 index = 0; message.FindMessage("service", index, 1419 &job) == B_OK; index++) { 1420 _AddJob(target, true, job); 1421 } 1422 1423 for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK; 1424 index++) { 1425 _AddJob(target, false, job); 1426 } 1427 } 1428 1429 1430 void 1431 LaunchDaemon::_AddTargets(BMessage& message) 1432 { 1433 BMessage targetMessage; 1434 for (int32 index = 0; message.FindMessage("target", index, 1435 &targetMessage) == B_OK; index++) { 1436 const char* name = targetMessage.GetString("name"); 1437 if (name == NULL) { 1438 // TODO: log error 1439 debug_printf("Target has no name, ignoring it!\n"); 1440 continue; 1441 } 1442 1443 Target* target = FindTarget(name); 1444 if (target == NULL) { 1445 target = new Target(name); 1446 _AddTarget(target); 1447 } else if (targetMessage.GetBool("reset")) { 1448 // Remove all jobs from this target 1449 for (JobMap::iterator iterator = fJobs.begin(); 1450 iterator != fJobs.end();) { 1451 Job* job = iterator->second; 1452 JobMap::iterator remove = iterator++; 1453 1454 if (job->Target() == target) { 1455 fJobs.erase(remove); 1456 delete job; 1457 } 1458 } 1459 } 1460 1461 _SetCondition(target, targetMessage); 1462 _SetEvent(target, targetMessage); 1463 _SetEnvironment(target, targetMessage); 1464 _AddJobs(target, targetMessage); 1465 1466 if (target->Event() != NULL) 1467 target->Event()->Register(*this); 1468 } 1469 } 1470 1471 1472 void 1473 LaunchDaemon::_AddRunTargets(BMessage& message) 1474 { 1475 BMessage runMessage; 1476 for (int32 index = 0; message.FindMessage("run", index, 1477 &runMessage) == B_OK; index++) { 1478 BMessage conditions; 1479 bool pass = true; 1480 if (runMessage.FindMessage("if", &conditions) == B_OK) { 1481 Condition* condition = Conditions::FromMessage(conditions); 1482 if (condition != NULL) { 1483 pass = condition->Test(*this); 1484 debug_printf("Test: %s -> %d\n", condition->ToString().String(), 1485 pass); 1486 delete condition; 1487 } else 1488 debug_printf("Could not parse condition!\n"); 1489 } 1490 1491 if (pass) { 1492 _AddRunTargets(runMessage, NULL); 1493 _AddRunTargets(runMessage, "then"); 1494 } else { 1495 _AddRunTargets(runMessage, "else"); 1496 } 1497 } 1498 } 1499 1500 1501 void 1502 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name) 1503 { 1504 BMessage targets; 1505 if (name != NULL && message.FindMessage(name, &targets) != B_OK) 1506 return; 1507 1508 const char* target; 1509 for (int32 index = 0; targets.FindString("target", index, &target) == B_OK; 1510 index++) { 1511 fRunTargets.Add(target); 1512 } 1513 } 1514 1515 1516 void 1517 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) 1518 { 1519 BString name = message.GetString("name"); 1520 if (name.IsEmpty()) { 1521 // Invalid job description 1522 return; 1523 } 1524 name.ToLower(); 1525 1526 Job* job = FindJob(name); 1527 if (job == NULL) { 1528 TRACE(" add job \"%s\"\n", name.String()); 1529 1530 job = new (std::nothrow) Job(name); 1531 if (job == NULL) 1532 return; 1533 1534 job->SetTeamListener(this); 1535 job->SetService(service); 1536 job->SetCreateDefaultPort(service); 1537 job->SetTarget(target); 1538 } else 1539 TRACE(" amend job \"%s\"\n", name.String()); 1540 1541 if (message.HasBool("disabled")) { 1542 job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled())); 1543 fLog.JobEnabled(job, job->IsEnabled()); 1544 } 1545 1546 if (message.HasBool("legacy")) 1547 job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); 1548 1549 _SetCondition(job, message); 1550 _SetEvent(job, message); 1551 _SetEnvironment(job, message); 1552 1553 BMessage portMessage; 1554 for (int32 index = 0; 1555 message.FindMessage("port", index, &portMessage) == B_OK; index++) { 1556 job->AddPort(portMessage); 1557 } 1558 1559 if (message.HasString("launch")) 1560 message.FindStrings("launch", &job->Arguments()); 1561 1562 const char* requirement; 1563 for (int32 index = 0; 1564 message.FindString("requires", index, &requirement) == B_OK; 1565 index++) { 1566 job->AddRequirement(requirement); 1567 } 1568 if (fInitTarget != NULL) 1569 job->AddRequirement(fInitTarget->Name()); 1570 1571 fJobs.insert(std::make_pair(job->Title(), job)); 1572 } 1573 1574 1575 /*! Initializes all jobs for the specified target (may be \c NULL). 1576 Jobs that cannot be initialized, and those that never will be due to 1577 conditions, will be removed from the list. 1578 */ 1579 void 1580 LaunchDaemon::_InitJobs(Target* target) 1581 { 1582 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) { 1583 Job* job = iterator->second; 1584 JobMap::iterator remove = iterator++; 1585 1586 if (job->Target() != target) 1587 continue; 1588 1589 status_t status = B_NO_INIT; 1590 if (job->IsEnabled()) { 1591 // Filter out jobs that have a constant and failing condition 1592 if (job->Condition() == NULL || !job->Condition()->IsConstant(*this) 1593 || job->Condition()->Test(*this)) { 1594 std::set<BString> dependencies; 1595 status = job->Init(*this, dependencies); 1596 if (status == B_OK && job->Event() != NULL) 1597 status = job->Event()->Register(*this); 1598 } 1599 } 1600 1601 if (status == B_OK) { 1602 fLog.JobInitialized(job); 1603 } else { 1604 if (status != B_NO_INIT) { 1605 // TODO: log error 1606 debug_printf("Init \"%s\" failed: %s\n", job->Name(), 1607 strerror(status)); 1608 } 1609 fLog.JobIgnored(job, status); 1610 1611 // Remove jobs that won't be used later on 1612 fJobs.erase(remove); 1613 delete job; 1614 } 1615 } 1616 } 1617 1618 1619 /*! Adds all jobs for the specified target (may be \c NULL) to the launch 1620 queue, except those that are triggered by events that haven't been 1621 triggered yet. 1622 1623 Unless \a forceNow is true, the target is only launched if its events, 1624 if any, have been triggered already, and its conditions are met. 1625 */ 1626 void 1627 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow) 1628 { 1629 if (!forceNow && target != NULL && (!target->EventHasTriggered() 1630 || !target->CheckCondition(*this))) { 1631 return; 1632 } 1633 1634 if (target != NULL && !target->HasLaunched()) { 1635 target->SetLaunched(true); 1636 _InitJobs(target); 1637 } 1638 1639 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1640 iterator++) { 1641 Job* job = iterator->second; 1642 if (job->Target() == target) 1643 _LaunchJob(job); 1644 } 1645 } 1646 1647 1648 /*! Stops all running jobs of the specified target (may be \c NULL). 1649 */ 1650 void 1651 LaunchDaemon::_StopJobs(Target* target, bool force) 1652 { 1653 if (target != NULL && !target->HasLaunched()) 1654 return; 1655 1656 for (JobMap::reverse_iterator iterator = fJobs.rbegin(); 1657 iterator != fJobs.rend(); iterator++) { 1658 Job* job = iterator->second; 1659 if (job->Target() == target) 1660 _StopJob(job, force); 1661 } 1662 } 1663 1664 1665 /*! Checks whether or not the specified \a job can be launched. 1666 If \a testOnly is \c false, calling this method will trigger a demand 1667 to the \a job. 1668 */ 1669 bool 1670 LaunchDaemon::_CanLaunchJob(Job* job, uint32 options, bool testOnly) 1671 { 1672 if (job == NULL || !job->CanBeLaunched()) 1673 return false; 1674 1675 return (options & FORCE_NOW) != 0 1676 || (job->EventHasTriggered() && job->CheckCondition(*this) 1677 && ((options & TRIGGER_DEMAND) == 0 1678 || Events::TriggerDemand(job->Event(), testOnly))); 1679 } 1680 1681 1682 /*! Checks recursively if the requirements of the specified job can be launched, 1683 if they are not running already. 1684 Calling this method will not trigger a demand for the requirements. 1685 */ 1686 bool 1687 LaunchDaemon::_CanLaunchJobRequirements(Job* job, uint32 options) 1688 { 1689 int32 count = job->Requirements().CountStrings(); 1690 for (int32 index = 0; index < count; index++) { 1691 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1692 if (requirement != NULL 1693 && !requirement->IsRunning() && !requirement->IsLaunching() 1694 && (!_CanLaunchJob(requirement, options, true) 1695 || _CanLaunchJobRequirements(requirement, options))) { 1696 requirement->AddPending(job->Name()); 1697 return false; 1698 } 1699 } 1700 1701 return true; 1702 } 1703 1704 1705 /*! Adds the specified \a job to the launch queue 1706 queue, except those that are triggered by events. 1707 1708 Unless \c FORCE_NOW is set, the target is only launched if its events, 1709 if any, have been triggered already. 1710 1711 Calling this method will trigger a demand event if \c TRIGGER_DEMAND has 1712 been set. 1713 */ 1714 bool 1715 LaunchDaemon::_LaunchJob(Job* job, uint32 options) 1716 { 1717 if (job != NULL && (job->IsLaunching() || job->IsRunning())) 1718 return true; 1719 1720 if (!_CanLaunchJob(job, options)) 1721 return false; 1722 1723 // Test if we can launch all requirements 1724 if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND)) 1725 return false; 1726 1727 // Actually launch the requirements 1728 int32 count = job->Requirements().CountStrings(); 1729 for (int32 index = 0; index < count; index++) { 1730 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1731 if (requirement != NULL) { 1732 // TODO: For jobs that have their communication channels set up, 1733 // we would not need to trigger demand at this point 1734 if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) { 1735 // Failed to put a requirement into the launch queue 1736 return false; 1737 } 1738 } 1739 } 1740 1741 if (job->Target() != NULL) 1742 job->Target()->ResolveSourceFiles(); 1743 if (job->Event() != NULL) 1744 job->Event()->ResetTrigger(); 1745 1746 job->SetLaunching(true); 1747 1748 status_t status = fJobQueue.AddJob(job); 1749 if (status != B_OK) { 1750 debug_printf("Adding job %s to queue failed: %s\n", job->Name(), 1751 strerror(status)); 1752 return false; 1753 } 1754 1755 // Try to launch pending jobs as well 1756 count = job->Pending().CountStrings(); 1757 for (int32 index = 0; index < count; index++) { 1758 Job* pending = FindJob(job->Pending().StringAt(index)); 1759 if (pending != NULL && _LaunchJob(pending, 0)) { 1760 // Remove the job from the pending list once its in the launch 1761 // queue, so that is not being launched again next time. 1762 index--; 1763 count--; 1764 } 1765 } 1766 1767 return true; 1768 } 1769 1770 1771 void 1772 LaunchDaemon::_StopJob(Job* job, bool force) 1773 { 1774 // TODO: find out which jobs require this job, and don't stop if any, 1775 // unless force, and then stop them all. 1776 job->SetEnabled(false); 1777 1778 if (!job->IsRunning()) 1779 return; 1780 1781 // Be nice first, and send a simple quit message 1782 BMessenger messenger; 1783 if (job->GetMessenger(messenger) == B_OK) { 1784 BMessage request(B_QUIT_REQUESTED); 1785 messenger.SendMessage(&request); 1786 1787 // TODO: wait a bit before going further 1788 return; 1789 } 1790 // TODO: allow custom shutdown 1791 1792 send_signal(-job->Team(), SIGINT); 1793 // TODO: this would be the next step, again, after a delay 1794 //send_signal(job->Team(), SIGKILL); 1795 } 1796 1797 1798 void 1799 LaunchDaemon::_AddTarget(Target* target) 1800 { 1801 fTargets.insert(std::make_pair(target->Title(), target)); 1802 } 1803 1804 1805 void 1806 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message) 1807 { 1808 Condition* condition = job->Condition(); 1809 bool updated = false; 1810 1811 BMessage conditions; 1812 if (message.FindMessage("if", &conditions) == B_OK) { 1813 condition = Conditions::FromMessage(conditions); 1814 updated = true; 1815 } 1816 1817 if (message.GetBool("no_safemode")) { 1818 condition = Conditions::AddNotSafeMode(condition); 1819 updated = true; 1820 } 1821 1822 if (updated) 1823 job->SetCondition(condition); 1824 } 1825 1826 1827 void 1828 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message) 1829 { 1830 Event* event = job->Event(); 1831 bool updated = false; 1832 1833 BMessage events; 1834 if (message.FindMessage("on", &events) == B_OK) { 1835 event = Events::FromMessage(this, events); 1836 updated = true; 1837 } 1838 1839 if (message.GetBool("on_demand")) { 1840 event = Events::AddOnDemand(this, event); 1841 updated = true; 1842 } 1843 1844 if (updated) { 1845 TRACE(" event: %s\n", event->ToString().String()); 1846 job->SetEvent(event); 1847 _ResolveExternalEvents(job); 1848 } 1849 } 1850 1851 1852 void 1853 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message) 1854 { 1855 BMessage environmentMessage; 1856 if (message.FindMessage("env", &environmentMessage) == B_OK) 1857 job->SetEnvironment(environmentMessage); 1858 } 1859 1860 1861 ExternalEventSource* 1862 LaunchDaemon::_FindEvent(const char* owner, const char* name) const 1863 { 1864 if (name == NULL) 1865 return NULL; 1866 1867 BString eventName = name; 1868 eventName.ToLower(); 1869 1870 EventMap::const_iterator found = fEvents.find(eventName); 1871 if (found != fEvents.end()) 1872 return found->second; 1873 1874 if (owner == NULL) 1875 return NULL; 1876 1877 eventName.Prepend("/"); 1878 eventName.Prepend(get_leaf(owner)); 1879 1880 found = fEvents.find(eventName); 1881 if (found != fEvents.end()) 1882 return found->second; 1883 1884 return NULL; 1885 } 1886 1887 1888 void 1889 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event, 1890 const BString& name) 1891 { 1892 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1893 iterator++) { 1894 Job* job = iterator->second; 1895 if (Events::ResolveExternalEvent(job->Event(), name, event->Flags())) 1896 event->AddListener(job); 1897 } 1898 } 1899 1900 1901 void 1902 LaunchDaemon::_ResolveExternalEvents(BaseJob* job) 1903 { 1904 if (job->Event() == NULL) 1905 return; 1906 1907 for (EventMap::iterator iterator = fEvents.begin(); 1908 iterator != fEvents.end(); iterator++) { 1909 ExternalEventSource* event = iterator->second; 1910 if (Events::ResolveExternalEvent(job->Event(), event->Name(), 1911 event->Flags())) 1912 event->AddListener(job); 1913 } 1914 } 1915 1916 1917 void 1918 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info) 1919 { 1920 info.SetString("name", job->Name()); 1921 1922 if (job->Event() != NULL) 1923 info.SetString("event", job->Event()->ToString()); 1924 1925 if (job->Condition() != NULL) 1926 info.SetString("condition", job->Condition()->ToString()); 1927 } 1928 1929 1930 void 1931 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message) 1932 { 1933 if (fUserMode) 1934 return; 1935 1936 // Forward event to user launch_daemon(s) 1937 if (user == 0) { 1938 for (SessionMap::iterator iterator = fSessions.begin(); 1939 iterator != fSessions.end(); iterator++) { 1940 Session* session = iterator->second; 1941 session->Daemon().SendMessage(message); 1942 // ignore reply 1943 } 1944 } else { 1945 Session* session = FindSession(user); 1946 if (session != NULL) 1947 session->Daemon().SendMessage(message); 1948 } 1949 } 1950 1951 1952 status_t 1953 LaunchDaemon::_StartSession(const char* login) 1954 { 1955 // TODO: enable user/group code 1956 // The launch_daemon currently cannot talk to the registrar, though 1957 1958 struct passwd* passwd = getpwnam(login); 1959 if (passwd == NULL) 1960 return B_NAME_NOT_FOUND; 1961 if (strcmp(passwd->pw_name, login) != 0) 1962 return B_NAME_NOT_FOUND; 1963 1964 // Check if there is a user session running already 1965 uid_t user = passwd->pw_uid; 1966 gid_t group = passwd->pw_gid; 1967 1968 Unlock(); 1969 1970 if (fork() == 0) { 1971 if (setsid() < 0) 1972 exit(EXIT_FAILURE); 1973 1974 if (initgroups(login, group) == -1) 1975 exit(EXIT_FAILURE); 1976 if (setgid(group) != 0) 1977 exit(EXIT_FAILURE); 1978 if (setuid(user) != 0) 1979 exit(EXIT_FAILURE); 1980 1981 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') { 1982 setenv("HOME", passwd->pw_dir, true); 1983 1984 if (chdir(passwd->pw_dir) != 0) { 1985 debug_printf("Could not switch to home dir %s: %s\n", 1986 passwd->pw_dir, strerror(errno)); 1987 } 1988 } 1989 1990 // TODO: This leaks the parent application 1991 be_app = NULL; 1992 1993 // Reinitialize be_roster 1994 BRoster::Private().DeleteBeRoster(); 1995 BRoster::Private().InitBeRoster(); 1996 1997 // TODO: take over system jobs, and reserve their names (or ask parent) 1998 status_t status; 1999 LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status); 2000 if (status == B_OK) 2001 daemon->Run(); 2002 2003 delete daemon; 2004 exit(EXIT_SUCCESS); 2005 } 2006 Lock(); 2007 return B_OK; 2008 } 2009 2010 2011 void 2012 LaunchDaemon::_RetrieveKernelOptions() 2013 { 2014 char buffer[32]; 2015 size_t size = sizeof(buffer); 2016 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer, 2017 &size); 2018 if (status == B_OK) { 2019 fSafeMode = !strncasecmp(buffer, "true", size) 2020 || !strncasecmp(buffer, "yes", size) 2021 || !strncasecmp(buffer, "on", size) 2022 || !strncasecmp(buffer, "enabled", size); 2023 } else 2024 fSafeMode = false; 2025 } 2026 2027 2028 void 2029 LaunchDaemon::_SetupEnvironment() 2030 { 2031 // Determine safemode kernel option 2032 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true); 2033 2034 // Default locale settings 2035 setenv("LC_TYPE", "en_US.UTF-8", true); 2036 } 2037 2038 2039 /*! Basic system initialization that must happen before any jobs are launched. 2040 */ 2041 void 2042 LaunchDaemon::_InitSystem() 2043 { 2044 #ifndef TEST_MODE 2045 _AddInitJob(new InitRealTimeClockJob()); 2046 _AddInitJob(new InitSharedMemoryDirectoryJob()); 2047 _AddInitJob(new InitTemporaryDirectoryJob()); 2048 #endif 2049 2050 fJobQueue.AddJob(fInitTarget); 2051 } 2052 2053 2054 void 2055 LaunchDaemon::_AddInitJob(BJob* job) 2056 { 2057 fInitTarget->AddDependency(job); 2058 fJobQueue.AddJob(job); 2059 } 2060 2061 2062 // #pragma mark - 2063 2064 2065 #ifndef TEST_MODE 2066 2067 2068 static void 2069 open_stdio(int targetFD, int openMode) 2070 { 2071 #ifdef DEBUG 2072 int fd = open("/dev/dprintf", openMode); 2073 #else 2074 int fd = open("/dev/null", openMode); 2075 #endif 2076 if (fd != targetFD) { 2077 dup2(fd, targetFD); 2078 close(fd); 2079 } 2080 } 2081 2082 2083 #endif // TEST_MODE 2084 2085 2086 int 2087 main() 2088 { 2089 if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) { 2090 fprintf(stderr, "The launch_daemon is already running!\n"); 2091 return EXIT_FAILURE; 2092 } 2093 2094 #ifndef TEST_MODE 2095 // Make stdin/out/err available 2096 open_stdio(STDIN_FILENO, O_RDONLY); 2097 open_stdio(STDOUT_FILENO, O_WRONLY); 2098 dup2(STDOUT_FILENO, STDERR_FILENO); 2099 #endif 2100 2101 EventMap events; 2102 status_t status; 2103 LaunchDaemon* daemon = new LaunchDaemon(false, events, status); 2104 if (status == B_OK) 2105 daemon->Run(); 2106 2107 delete daemon; 2108 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 2109 } 2110