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