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