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 return false; 1467 } 1468 } 1469 1470 return true; 1471 } 1472 1473 1474 /*! Adds the specified \a job to the launch queue 1475 queue, except those that are triggered by events. 1476 1477 Unless \c FORCE_NOW is set, the target is only launched if its events, 1478 if any, have been triggered already. 1479 1480 Calling this method will trigger a demand event if \c TRIGGER_DEMAND has 1481 been set. 1482 */ 1483 bool 1484 LaunchDaemon::_LaunchJob(Job* job, uint32 options) 1485 { 1486 if (job != NULL && (job->IsLaunching() || job->IsRunning())) 1487 return true; 1488 1489 if (!_CanLaunchJob(job, options)) 1490 return false; 1491 1492 // Test if we can launch all requirements 1493 if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND)) 1494 return false; 1495 1496 // Actually launch the requirements 1497 int32 count = job->Requirements().CountStrings(); 1498 for (int32 index = 0; index < count; index++) { 1499 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1500 if (requirement != NULL) { 1501 // TODO: For jobs that have their communication channels set up, 1502 // we would not need to trigger demand at this point 1503 if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) { 1504 // Failed to put a requirement into the launch queue 1505 return false; 1506 } 1507 } 1508 } 1509 1510 if (job->Target() != NULL) 1511 job->Target()->ResolveSourceFiles(); 1512 if (job->Event() != NULL) 1513 job->Event()->ResetTrigger(); 1514 1515 job->SetLaunching(true); 1516 1517 status_t status = fJobQueue.AddJob(job); 1518 if (status != B_OK) { 1519 debug_printf("Adding job %s to queue failed: %s\n", job->Name(), 1520 strerror(status)); 1521 return false; 1522 } 1523 1524 return true; 1525 } 1526 1527 1528 void 1529 LaunchDaemon::_StopJob(Job* job, bool force) 1530 { 1531 // TODO: find out which jobs require this job, and don't stop if any, 1532 // unless force, and then stop them all. 1533 job->SetEnabled(false); 1534 1535 if (!job->IsRunning()) 1536 return; 1537 1538 // Be nice first, and send a simple quit message 1539 BMessenger messenger; 1540 if (job->GetMessenger(messenger) == B_OK) { 1541 BMessage request(B_QUIT_REQUESTED); 1542 messenger.SendMessage(&request); 1543 1544 // TODO: wait a bit before going further 1545 return; 1546 } 1547 // TODO: allow custom shutdown 1548 1549 send_signal(-job->Team(), SIGINT); 1550 // TODO: this would be the next step, again, after a delay 1551 //send_signal(job->Team(), SIGKILL); 1552 } 1553 1554 1555 void 1556 LaunchDaemon::_AddTarget(Target* target) 1557 { 1558 fTargets.insert(std::make_pair(target->Title(), target)); 1559 } 1560 1561 1562 void 1563 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message) 1564 { 1565 Condition* condition = job->Condition(); 1566 bool updated = false; 1567 1568 BMessage conditions; 1569 if (message.FindMessage("if", &conditions) == B_OK) { 1570 condition = Conditions::FromMessage(conditions); 1571 updated = true; 1572 } 1573 1574 if (message.GetBool("no_safemode")) { 1575 condition = Conditions::AddNotSafeMode(condition); 1576 updated = true; 1577 } 1578 1579 if (updated) 1580 job->SetCondition(condition); 1581 } 1582 1583 1584 void 1585 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message) 1586 { 1587 Event* event = job->Event(); 1588 bool updated = false; 1589 1590 BMessage events; 1591 if (message.FindMessage("on", &events) == B_OK) { 1592 event = Events::FromMessage(this, events); 1593 updated = true; 1594 } 1595 1596 if (message.GetBool("on_demand")) { 1597 event = Events::AddOnDemand(this, event); 1598 updated = true; 1599 } 1600 1601 if (updated) { 1602 TRACE(" event: %s\n", event->ToString().String()); 1603 job->SetEvent(event); 1604 _ResolveExternalEvents(job); 1605 } 1606 } 1607 1608 1609 void 1610 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message) 1611 { 1612 BMessage environmentMessage; 1613 if (message.FindMessage("env", &environmentMessage) == B_OK) 1614 job->SetEnvironment(environmentMessage); 1615 } 1616 1617 1618 ExternalEventSource* 1619 LaunchDaemon::_FindEvent(const char* owner, const char* name) const 1620 { 1621 if (name == NULL) 1622 return NULL; 1623 1624 BString eventName = name; 1625 eventName.ToLower(); 1626 1627 EventMap::const_iterator found = fEvents.find(eventName); 1628 if (found != fEvents.end()) 1629 return found->second; 1630 1631 if (owner == NULL) 1632 return NULL; 1633 1634 eventName.Prepend("/"); 1635 eventName.Prepend(get_leaf(owner)); 1636 1637 found = fEvents.find(eventName); 1638 if (found != fEvents.end()) 1639 return found->second; 1640 1641 return NULL; 1642 } 1643 1644 1645 void 1646 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event, 1647 const BString& name) 1648 { 1649 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1650 iterator++) { 1651 Job* job = iterator->second; 1652 if (Events::ResolveExternalEvent(job->Event(), name, event->Flags())) 1653 event->AddListener(job); 1654 } 1655 } 1656 1657 1658 void 1659 LaunchDaemon::_ResolveExternalEvents(BaseJob* job) 1660 { 1661 if (job->Event() == NULL) 1662 return; 1663 1664 for (EventMap::iterator iterator = fEvents.begin(); 1665 iterator != fEvents.end(); iterator++) { 1666 ExternalEventSource* event = iterator->second; 1667 if (Events::ResolveExternalEvent(job->Event(), event->Name(), 1668 event->Flags())) 1669 event->AddListener(job); 1670 } 1671 } 1672 1673 1674 void 1675 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info) 1676 { 1677 info.SetString("name", job->Name()); 1678 1679 if (job->Event() != NULL) 1680 info.SetString("event", job->Event()->ToString()); 1681 1682 if (job->Condition() != NULL) 1683 info.SetString("condition", job->Condition()->ToString()); 1684 } 1685 1686 1687 void 1688 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message) 1689 { 1690 if (fUserMode) 1691 return; 1692 1693 // Forward event to user launch_daemon(s) 1694 if (user == 0) { 1695 for (SessionMap::iterator iterator = fSessions.begin(); 1696 iterator != fSessions.end(); iterator++) { 1697 Session* session = iterator->second; 1698 session->Daemon().SendMessage(message); 1699 // ignore reply 1700 } 1701 } else { 1702 Session* session = FindSession(user); 1703 if (session != NULL) 1704 session->Daemon().SendMessage(message); 1705 } 1706 } 1707 1708 1709 status_t 1710 LaunchDaemon::_StartSession(const char* login) 1711 { 1712 // TODO: enable user/group code 1713 // The launch_daemon currently cannot talk to the registrar, though 1714 1715 struct passwd* passwd = getpwnam(login); 1716 if (passwd == NULL) 1717 return B_NAME_NOT_FOUND; 1718 if (strcmp(passwd->pw_name, login) != 0) 1719 return B_NAME_NOT_FOUND; 1720 1721 // Check if there is a user session running already 1722 uid_t user = passwd->pw_uid; 1723 gid_t group = passwd->pw_gid; 1724 1725 Unlock(); 1726 1727 if (fork() == 0) { 1728 if (setsid() < 0) 1729 exit(EXIT_FAILURE); 1730 1731 if (initgroups(login, group) == -1) 1732 exit(EXIT_FAILURE); 1733 if (setgid(group) != 0) 1734 exit(EXIT_FAILURE); 1735 if (setuid(user) != 0) 1736 exit(EXIT_FAILURE); 1737 1738 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') { 1739 setenv("HOME", passwd->pw_dir, true); 1740 1741 if (chdir(passwd->pw_dir) != 0) { 1742 debug_printf("Could not switch to home dir %s: %s\n", 1743 passwd->pw_dir, strerror(errno)); 1744 } 1745 } 1746 1747 // TODO: This leaks the parent application 1748 be_app = NULL; 1749 1750 // Reinitialize be_roster 1751 BRoster::Private().DeleteBeRoster(); 1752 BRoster::Private().InitBeRoster(); 1753 1754 // TODO: take over system jobs, and reserve their names (or ask parent) 1755 status_t status; 1756 LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status); 1757 if (status == B_OK) 1758 daemon->Run(); 1759 1760 delete daemon; 1761 exit(EXIT_SUCCESS); 1762 } 1763 Lock(); 1764 return B_OK; 1765 } 1766 1767 1768 void 1769 LaunchDaemon::_RetrieveKernelOptions() 1770 { 1771 char buffer[32]; 1772 size_t size = sizeof(buffer); 1773 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer, 1774 &size); 1775 if (status == B_OK) { 1776 fSafeMode = !strncasecmp(buffer, "true", size) 1777 || !strncasecmp(buffer, "yes", size) 1778 || !strncasecmp(buffer, "on", size) 1779 || !strncasecmp(buffer, "enabled", size); 1780 } else 1781 fSafeMode = false; 1782 } 1783 1784 1785 void 1786 LaunchDaemon::_SetupEnvironment() 1787 { 1788 // Determine safemode kernel option 1789 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true); 1790 1791 // Default locale settings 1792 setenv("LC_TYPE", "en_US.UTF-8", true); 1793 } 1794 1795 1796 /*! Basic system initialization that must happen before any jobs are launched. 1797 */ 1798 void 1799 LaunchDaemon::_InitSystem() 1800 { 1801 _AddInitJob(new InitRealTimeClockJob()); 1802 _AddInitJob(new InitSharedMemoryDirectoryJob()); 1803 _AddInitJob(new InitTemporaryDirectoryJob()); 1804 1805 fJobQueue.AddJob(fInitTarget); 1806 } 1807 1808 1809 void 1810 LaunchDaemon::_AddInitJob(BJob* job) 1811 { 1812 fInitTarget->AddDependency(job); 1813 fJobQueue.AddJob(job); 1814 } 1815 1816 1817 // #pragma mark - 1818 1819 1820 static void 1821 open_stdio(int targetFD, int openMode) 1822 { 1823 #ifdef DEBUG 1824 int fd = open("/dev/dprintf", openMode); 1825 #else 1826 int fd = open("/dev/null", openMode); 1827 #endif 1828 if (fd != targetFD) { 1829 dup2(fd, targetFD); 1830 close(fd); 1831 } 1832 } 1833 1834 1835 int 1836 main() 1837 { 1838 // Make stdin/out/err available 1839 open_stdio(STDIN_FILENO, O_RDONLY); 1840 open_stdio(STDOUT_FILENO, O_WRONLY); 1841 dup2(STDOUT_FILENO, STDERR_FILENO); 1842 1843 EventMap events; 1844 status_t status; 1845 LaunchDaemon* daemon = new LaunchDaemon(false, events, status); 1846 if (status == B_OK) 1847 daemon->Run(); 1848 1849 delete daemon; 1850 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 1851 } 1852