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