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