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 <RosterPrivate.h> 31 #include <syscalls.h> 32 33 #include "multiuser_utils.h" 34 35 #include "Conditions.h" 36 #include "Events.h" 37 #include "InitRealTimeClockJob.h" 38 #include "InitSharedMemoryDirectoryJob.h" 39 #include "InitTemporaryDirectoryJob.h" 40 #include "Job.h" 41 #include "SettingsParser.h" 42 #include "Target.h" 43 #include "Utility.h" 44 #include "Worker.h" 45 46 47 #ifdef DEBUG 48 # define TRACE(x, ...) debug_printf(x, __VA_ARGS__) 49 #else 50 # define TRACE(x, ...) ; 51 #endif 52 53 54 using namespace ::BPrivate; 55 using namespace BSupportKit; 56 using BSupportKit::BPrivate::JobQueue; 57 58 59 static const char* kLaunchDirectory = "launch"; 60 static const char* kUserLaunchDirectory = "user_launch"; 61 62 63 enum launch_options { 64 FORCE_NOW = 0x01, 65 TRIGGER_DEMAND = 0x02 66 }; 67 68 69 class Session { 70 public: 71 Session(uid_t user, const BMessenger& target); 72 73 uid_t User() const 74 { return fUser; } 75 const BMessenger& Daemon() const 76 { return fDaemon; } 77 78 private: 79 uid_t fUser; 80 BMessenger fDaemon; 81 }; 82 83 84 /*! This class is the connection between the external events that are part of 85 a job, and the external event source. 86 87 There is one object per registered event source, and it keeps all jobs that 88 reference the event as listeners. If the event source triggers the event, 89 the object will be used to trigger the jobs. 90 */ 91 class ExternalEventSource { 92 public: 93 ExternalEventSource(BMessenger& source, 94 const char* ownerName, 95 const char* name, uint32 flags); 96 ~ExternalEventSource(); 97 98 const char* Name() const; 99 uint32 Flags() const 100 { return fFlags; } 101 102 int32 CountListeners() const; 103 BaseJob* ListenerAt(int32 index) const; 104 105 status_t AddListener(BaseJob* job); 106 void RemoveListener(BaseJob* job); 107 108 private: 109 BString fName; 110 uint32 fFlags; 111 BObjectList<BaseJob> fListeners; 112 }; 113 114 115 typedef std::map<BString, Job*> JobMap; 116 typedef std::map<uid_t, Session*> SessionMap; 117 typedef std::map<BString, Target*> TargetMap; 118 typedef std::map<BString, ExternalEventSource*> EventMap; 119 120 121 class LaunchDaemon : public BServer, public Finder, public ConditionContext, 122 public EventRegistrator { 123 public: 124 LaunchDaemon(bool userMode, 125 const EventMap& events, status_t& error); 126 virtual ~LaunchDaemon(); 127 128 virtual Job* FindJob(const char* name) const; 129 virtual Target* FindTarget(const char* name) const; 130 Session* FindSession(uid_t user) const; 131 132 // ConditionContext 133 virtual bool IsSafeMode() const; 134 virtual bool BootVolumeIsReadOnly() const; 135 136 // EventRegistrator 137 virtual status_t RegisterExternalEvent(Event* event, 138 const char* name, 139 const BStringList& arguments); 140 virtual void UnregisterExternalEvent(Event* event, 141 const char* name); 142 143 virtual void ReadyToRun(); 144 virtual void MessageReceived(BMessage* message); 145 146 private: 147 void _HandleGetLaunchData(BMessage* message); 148 void _HandleLaunchTarget(BMessage* message); 149 void _HandleLaunchSession(BMessage* message); 150 void _HandleRegisterSessionDaemon(BMessage* message); 151 void _HandleRegisterLaunchEvent(BMessage* message); 152 void _HandleUnregisterLaunchEvent(BMessage* message); 153 void _HandleNotifyLaunchEvent(BMessage* message); 154 void _HandleResetStickyLaunchEvent( 155 BMessage* message); 156 uid_t _GetUserID(BMessage* message); 157 158 void _ReadPaths(const BStringList& paths); 159 void _ReadEntry(const char* context, BEntry& entry); 160 void _ReadDirectory(const char* context, 161 BEntry& directory); 162 status_t _ReadFile(const char* context, BEntry& entry); 163 164 void _AddJobs(Target* target, BMessage& message); 165 void _AddTargets(BMessage& message); 166 void _AddRunTargets(BMessage& message); 167 void _AddRunTargets(BMessage& message, 168 const char* name); 169 void _AddJob(Target* target, bool service, 170 BMessage& message); 171 void _InitJobs(Target* target); 172 void _LaunchJobs(Target* target, 173 bool forceNow = false); 174 void _LaunchJob(Job* job, uint32 options = 0); 175 void _AddTarget(Target* target); 176 void _SetCondition(BaseJob* job, 177 const BMessage& message); 178 void _SetEvent(BaseJob* job, 179 const BMessage& message); 180 void _SetEnvironment(BaseJob* job, 181 const BMessage& message); 182 183 ExternalEventSource* 184 _FindEvent(const char* owner, 185 const char* name) const; 186 void _ResolveExternalEvents( 187 ExternalEventSource* event, 188 const BString& name); 189 void _ResolveExternalEvents(BaseJob* job); 190 void _GetTargetInfo(Target* target, BMessage& info); 191 void _GetJobInfo(Job* job, BMessage& info); 192 void _ForwardEventMessage(uid_t user, 193 BMessage* message); 194 195 status_t _StartSession(const char* login); 196 197 void _RetrieveKernelOptions(); 198 void _SetupEnvironment(); 199 void _InitSystem(); 200 void _AddInitJob(BJob* job); 201 202 private: 203 JobMap fJobs; 204 TargetMap fTargets; 205 BStringList fRunTargets; 206 EventMap fEvents; 207 JobQueue fJobQueue; 208 SessionMap fSessions; 209 MainWorker* fMainWorker; 210 Target* fInitTarget; 211 bool fSafeMode; 212 bool fReadOnlyBootVolume; 213 bool fUserMode; 214 }; 215 216 217 static const char* 218 get_leaf(const char* signature) 219 { 220 const char* separator = strrchr(signature, '/'); 221 if (separator != NULL) 222 return separator + 1; 223 224 return signature; 225 } 226 227 228 // #pragma mark - 229 230 231 Session::Session(uid_t user, const BMessenger& daemon) 232 : 233 fUser(user), 234 fDaemon(daemon) 235 { 236 } 237 238 239 // #pragma mark - 240 241 242 ExternalEventSource::ExternalEventSource(BMessenger& source, 243 const char* ownerName, const char* name, uint32 flags) 244 : 245 fName(name), 246 fFlags(flags), 247 fListeners(5, true) 248 { 249 } 250 251 252 ExternalEventSource::~ExternalEventSource() 253 { 254 } 255 256 257 const char* 258 ExternalEventSource::Name() const 259 { 260 return fName.String(); 261 } 262 263 264 int32 265 ExternalEventSource::CountListeners() const 266 { 267 return fListeners.CountItems(); 268 } 269 270 271 BaseJob* 272 ExternalEventSource::ListenerAt(int32 index) const 273 { 274 return fListeners.ItemAt(index); 275 } 276 277 278 status_t 279 ExternalEventSource::AddListener(BaseJob* job) 280 { 281 if (fListeners.AddItem(job)) 282 return B_OK; 283 284 return B_NO_MEMORY; 285 } 286 287 288 void 289 ExternalEventSource::RemoveListener(BaseJob* job) 290 { 291 fListeners.RemoveItem(job); 292 } 293 294 295 // #pragma mark - 296 297 298 LaunchDaemon::LaunchDaemon(bool userMode, const EventMap& events, 299 status_t& error) 300 : 301 BServer(kLaunchDaemonSignature, NULL, 302 create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, 303 userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error), 304 fEvents(events), 305 fInitTarget(userMode ? NULL : new Target("init")), 306 fUserMode(userMode) 307 { 308 fMainWorker = new MainWorker(fJobQueue); 309 fMainWorker->Init(); 310 311 if (fInitTarget != NULL) 312 _AddTarget(fInitTarget); 313 314 // We may not be able to talk to the registrar 315 if (!fUserMode) 316 BRoster::Private().SetWithoutRegistrar(true); 317 } 318 319 320 LaunchDaemon::~LaunchDaemon() 321 { 322 } 323 324 325 Job* 326 LaunchDaemon::FindJob(const char* name) const 327 { 328 if (name == NULL) 329 return NULL; 330 331 JobMap::const_iterator found = fJobs.find(BString(name).ToLower()); 332 if (found != fJobs.end()) 333 return found->second; 334 335 return NULL; 336 } 337 338 339 Target* 340 LaunchDaemon::FindTarget(const char* name) const 341 { 342 if (name == NULL) 343 return NULL; 344 345 TargetMap::const_iterator found = fTargets.find(BString(name).ToLower()); 346 if (found != fTargets.end()) 347 return found->second; 348 349 return NULL; 350 } 351 352 353 Session* 354 LaunchDaemon::FindSession(uid_t user) const 355 { 356 SessionMap::const_iterator found = fSessions.find(user); 357 if (found != fSessions.end()) 358 return found->second; 359 360 return NULL; 361 } 362 363 364 bool 365 LaunchDaemon::IsSafeMode() const 366 { 367 return fSafeMode; 368 } 369 370 371 bool 372 LaunchDaemon::BootVolumeIsReadOnly() const 373 { 374 return fReadOnlyBootVolume; 375 } 376 377 378 status_t 379 LaunchDaemon::RegisterExternalEvent(Event* event, const char* name, 380 const BStringList& arguments) 381 { 382 // TODO: register actual event with event source 383 return B_OK; 384 } 385 386 387 void 388 LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name) 389 { 390 // TODO! 391 } 392 393 394 void 395 LaunchDaemon::ReadyToRun() 396 { 397 _RetrieveKernelOptions(); 398 _SetupEnvironment(); 399 400 fReadOnlyBootVolume = Utility::IsReadOnlyVolume("/boot"); 401 if (fReadOnlyBootVolume) 402 Utility::BlockMedia("/boot", true); 403 404 if (fUserMode) { 405 BLaunchRoster roster; 406 BLaunchRoster::Private(roster).RegisterSessionDaemon(this); 407 } else 408 _InitSystem(); 409 410 BStringList paths; 411 if (fUserMode) { 412 // System-wide user specific jobs 413 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kUserLaunchDirectory, 414 B_FIND_PATHS_SYSTEM_ONLY, paths); 415 _ReadPaths(paths); 416 } 417 418 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory, 419 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 420 _ReadPaths(paths); 421 422 if (fUserMode) { 423 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, 424 kUserLaunchDirectory, B_FIND_PATHS_SYSTEM_ONLY, paths); 425 _ReadPaths(paths); 426 } 427 428 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory, 429 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 430 _ReadPaths(paths); 431 432 _InitJobs(NULL); 433 _LaunchJobs(NULL); 434 435 // Launch run targets (ignores events) 436 for (int32 index = 0; index < fRunTargets.CountStrings(); index++) { 437 Target* target = FindTarget(fRunTargets.StringAt(index)); 438 if (target != NULL) 439 _LaunchJobs(target); 440 } 441 } 442 443 444 void 445 LaunchDaemon::MessageReceived(BMessage* message) 446 { 447 switch (message->what) { 448 case B_GET_LAUNCH_DATA: 449 _HandleGetLaunchData(message); 450 break; 451 452 case B_LAUNCH_TARGET: 453 _HandleLaunchTarget(message); 454 break; 455 456 case B_LAUNCH_SESSION: 457 _HandleLaunchSession(message); 458 break; 459 460 case B_REGISTER_SESSION_DAEMON: 461 _HandleRegisterSessionDaemon(message); 462 break; 463 464 case B_REGISTER_LAUNCH_EVENT: 465 _HandleRegisterLaunchEvent(message); 466 break; 467 468 case B_UNREGISTER_LAUNCH_EVENT: 469 _HandleUnregisterLaunchEvent(message); 470 break; 471 472 case B_NOTIFY_LAUNCH_EVENT: 473 _HandleNotifyLaunchEvent(message); 474 break; 475 476 case B_RESET_STICKY_LAUNCH_EVENT: 477 _HandleResetStickyLaunchEvent(message); 478 break; 479 480 case B_GET_LAUNCH_TARGETS: 481 { 482 uid_t user = _GetUserID(message); 483 if (user < 0) 484 return; 485 486 BMessage reply; 487 status_t status = B_OK; 488 489 if (!fUserMode) { 490 // Request the data from the user's daemon, too 491 Session* session = FindSession(user); 492 if (session != NULL) { 493 BMessage request(B_GET_LAUNCH_TARGETS); 494 status = request.AddInt32("user", 0); 495 if (status == B_OK) { 496 status = session->Daemon().SendMessage(&request, 497 &reply); 498 } 499 if (status == B_OK) 500 status = reply.what; 501 } else 502 status = B_NAME_NOT_FOUND; 503 } 504 505 if (status == B_OK) { 506 TargetMap::const_iterator iterator = fTargets.begin(); 507 for (; iterator != fTargets.end(); iterator++) 508 reply.AddString("target", iterator->first); 509 } 510 511 reply.what = status; 512 message->SendReply(&reply); 513 break; 514 } 515 case B_GET_LAUNCH_TARGET_INFO: 516 { 517 uid_t user = _GetUserID(message); 518 if (user < 0) 519 return; 520 521 const char* name = message->GetString("name"); 522 Target* target = FindTarget(name); 523 if (target == NULL && !fUserMode) { 524 _ForwardEventMessage(user, message); 525 break; 526 } 527 528 BMessage reply(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND)); 529 if (target != NULL) 530 _GetTargetInfo(target, reply); 531 message->SendReply(&reply); 532 break; 533 } 534 case B_GET_LAUNCH_JOBS: 535 { 536 uid_t user = _GetUserID(message); 537 if (user < 0) 538 return; 539 540 const char* targetName = message->GetString("target"); 541 542 BMessage reply; 543 status_t status = B_OK; 544 545 if (!fUserMode) { 546 // Request the data from the user's daemon, too 547 Session* session = FindSession(user); 548 if (session != NULL) { 549 BMessage request(B_GET_LAUNCH_JOBS); 550 status = request.AddInt32("user", 0); 551 if (status == B_OK && targetName != NULL) 552 status = request.AddString("target", targetName); 553 if (status == B_OK) { 554 status = session->Daemon().SendMessage(&request, 555 &reply); 556 } 557 if (status == B_OK) 558 status = reply.what; 559 } else 560 status = B_NAME_NOT_FOUND; 561 } 562 563 if (status == B_OK) { 564 JobMap::const_iterator iterator = fJobs.begin(); 565 for (; iterator != fJobs.end(); iterator++) { 566 Job* job = iterator->second; 567 if (targetName != NULL && (job->Target() == NULL 568 || job->Target()->Title() != targetName)) { 569 continue; 570 } 571 reply.AddString("job", iterator->first); 572 } 573 } 574 575 reply.what = status; 576 message->SendReply(&reply); 577 break; 578 } 579 case B_GET_LAUNCH_JOB_INFO: 580 { 581 uid_t user = _GetUserID(message); 582 if (user < 0) 583 return; 584 585 const char* name = message->GetString("name"); 586 Job* job = FindJob(name); 587 if (job == NULL && !fUserMode) { 588 _ForwardEventMessage(user, message); 589 break; 590 } 591 592 BMessage reply(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND)); 593 if (job != NULL) 594 _GetJobInfo(job, reply); 595 message->SendReply(&reply); 596 break; 597 } 598 599 case kMsgEventTriggered: 600 { 601 // An internal event has been triggered. 602 // Check if its job can be launched now. 603 const char* name = message->GetString("owner"); 604 if (name == NULL) 605 break; 606 607 Job* job = FindJob(name); 608 if (job != NULL) { 609 _LaunchJob(job); 610 break; 611 } 612 613 Target* target = FindTarget(name); 614 if (target != NULL) { 615 _LaunchJobs(target); 616 break; 617 } 618 break; 619 } 620 621 default: 622 BServer::MessageReceived(message); 623 break; 624 } 625 } 626 627 628 void 629 LaunchDaemon::_HandleGetLaunchData(BMessage* message) 630 { 631 uid_t user = _GetUserID(message); 632 if (user < 0) 633 return; 634 635 BMessage reply((uint32)B_OK); 636 bool launchJob = true; 637 638 Job* job = FindJob(get_leaf(message->GetString("name"))); 639 if (job == NULL) { 640 Session* session = FindSession(user); 641 if (session != NULL) { 642 // Forward request to user launch_daemon 643 if (session->Daemon().SendMessage(message) == B_OK) 644 return; 645 } 646 reply.what = B_NAME_NOT_FOUND; 647 } else if (job->IsService() && !job->IsLaunched()) { 648 if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) { 649 // The job exists, but cannot be started yet, as its 650 // conditions are not met; don't make it available yet 651 // TODO: we may not want to initialize jobs with conditions 652 // that aren't met yet 653 reply.what = B_NO_INIT; 654 } else if (job->Event() != NULL) { 655 if (!Events::TriggerDemand(job->Event())) { 656 // The job is not triggered by demand; we cannot start it now 657 reply.what = B_NO_INIT; 658 } else { 659 // The job has already been triggered, don't launch it again 660 launchJob = false; 661 } 662 } 663 } else 664 launchJob = false; 665 666 bool ownsMessage = false; 667 if (reply.what == B_OK) { 668 // Launch the job if it hasn't been launched already 669 if (launchJob) 670 _LaunchJob(job, TRIGGER_DEMAND); 671 672 DetachCurrentMessage(); 673 status_t result = job->HandleGetLaunchData(message); 674 if (result == B_OK) { 675 // Replying is delegated to the job. 676 return; 677 } 678 679 ownsMessage = true; 680 reply.what = result; 681 } 682 683 message->SendReply(&reply); 684 if (ownsMessage) 685 delete message; 686 } 687 688 689 void 690 LaunchDaemon::_HandleLaunchTarget(BMessage* message) 691 { 692 uid_t user = _GetUserID(message); 693 if (user < 0) 694 return; 695 696 const char* name = message->GetString("target"); 697 const char* baseName = message->GetString("base target"); 698 699 Target* target = FindTarget(name); 700 if (target == NULL && baseName != NULL) { 701 Target* baseTarget = FindTarget(baseName); 702 if (baseTarget != NULL) { 703 target = new Target(name); 704 705 // Copy all jobs with the base target into the new target 706 for (JobMap::iterator iterator = fJobs.begin(); 707 iterator != fJobs.end();) { 708 Job* job = iterator->second; 709 iterator++; 710 711 if (job->Target() == baseTarget) { 712 Job* copy = new Job(*job); 713 copy->SetTarget(target); 714 715 fJobs.insert(std::make_pair(copy->Name(), copy)); 716 } 717 } 718 } 719 } 720 if (target == NULL) { 721 Session* session = FindSession(user); 722 if (session != NULL) { 723 // Forward request to user launch_daemon 724 if (session->Daemon().SendMessage(message) == B_OK) 725 return; 726 } 727 728 BMessage reply(B_NAME_NOT_FOUND); 729 message->SendReply(&reply); 730 return; 731 } 732 733 BMessage data; 734 if (message->FindMessage("data", &data) == B_OK) 735 target->AddData(data.GetString("name"), data); 736 737 _LaunchJobs(target); 738 } 739 740 741 void 742 LaunchDaemon::_HandleLaunchSession(BMessage* message) 743 { 744 uid_t user = _GetUserID(message); 745 if (user < 0) 746 return; 747 748 status_t status = B_OK; 749 const char* login = message->GetString("login"); 750 if (login == NULL) 751 status = B_BAD_VALUE; 752 if (status == B_OK && user != 0) { 753 // Only the root user can start sessions 754 // TODO: we'd actually need to know the uid of the sender 755 status = B_PERMISSION_DENIED; 756 } 757 if (status == B_OK) 758 status = _StartSession(login); 759 760 BMessage reply((uint32)status); 761 message->SendReply(&reply); 762 } 763 764 765 void 766 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message) 767 { 768 uid_t user = _GetUserID(message); 769 if (user < 0) 770 return; 771 772 status_t status = B_OK; 773 774 BMessenger target; 775 if (message->FindMessenger("daemon", &target) != B_OK) 776 status = B_BAD_VALUE; 777 778 if (status == B_OK) { 779 Session* session = new (std::nothrow) Session(user, target); 780 if (session != NULL) 781 fSessions.insert(std::make_pair(user, session)); 782 else 783 status = B_NO_MEMORY; 784 } 785 786 BMessage reply((uint32)status); 787 message->SendReply(&reply); 788 } 789 790 791 void 792 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message) 793 { 794 uid_t user = _GetUserID(message); 795 if (user < 0) 796 return; 797 798 if (user == 0 || fUserMode) { 799 status_t status = B_OK; 800 801 const char* name = message->GetString("name"); 802 const char* ownerName = message->GetString("owner"); 803 uint32 flags = message->GetUInt32("flags", 0); 804 BMessenger source; 805 if (name != NULL && ownerName != NULL 806 && message->FindMessenger("source", &source) == B_OK) { 807 // Register event 808 ownerName = get_leaf(ownerName); 809 810 ExternalEventSource* event = new (std::nothrow) 811 ExternalEventSource(source, ownerName, name, flags); 812 if (event != NULL) { 813 // Use short name, and fully qualified name 814 BString eventName = name; 815 fEvents.insert(std::make_pair(eventName, event)); 816 _ResolveExternalEvents(event, eventName); 817 818 eventName.Prepend("/"); 819 eventName.Prepend(ownerName); 820 fEvents.insert(std::make_pair(eventName, event)); 821 _ResolveExternalEvents(event, eventName); 822 } else 823 status = B_NO_MEMORY; 824 } else 825 status = B_BAD_VALUE; 826 827 BMessage reply((uint32)status); 828 message->SendReply(&reply); 829 } 830 831 _ForwardEventMessage(user, message); 832 } 833 834 835 void 836 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message) 837 { 838 uid_t user = _GetUserID(message); 839 if (user < 0) 840 return; 841 842 if (user == 0 || fUserMode) { 843 status_t status = B_OK; 844 845 const char* name = message->GetString("name"); 846 const char* ownerName = message->GetString("owner"); 847 BMessenger source; 848 if (name != NULL && ownerName != NULL 849 && message->FindMessenger("source", &source) == B_OK) { 850 // Unregister short and fully qualified event name 851 ownerName = get_leaf(ownerName); 852 853 BString eventName = name; 854 fEvents.erase(eventName); 855 856 eventName.Prepend("/"); 857 eventName.Prepend(ownerName); 858 fEvents.erase(eventName); 859 } else 860 status = B_BAD_VALUE; 861 862 BMessage reply((uint32)status); 863 message->SendReply(&reply); 864 } 865 866 _ForwardEventMessage(user, message); 867 } 868 869 870 void 871 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message) 872 { 873 uid_t user = _GetUserID(message); 874 if (user < 0) 875 return; 876 877 if (user == 0 || fUserMode) { 878 // Trigger events 879 const char* name = message->GetString("name"); 880 const char* ownerName = message->GetString("owner"); 881 // TODO: support arguments (as selectors) 882 883 ExternalEventSource* event = _FindEvent(ownerName, name); 884 if (event != NULL) { 885 // Evaluate all of its jobs 886 int32 count = event->CountListeners(); 887 for (int32 index = 0; index < count; index++) { 888 BaseJob* listener = event->ListenerAt(index); 889 Events::TriggerExternalEvent(listener->Event(), name); 890 } 891 } 892 } 893 894 _ForwardEventMessage(user, message); 895 } 896 897 898 void 899 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message) 900 { 901 uid_t user = _GetUserID(message); 902 if (user < 0) 903 return; 904 905 if (user == 0 || fUserMode) { 906 // Reset sticky events 907 const char* name = message->GetString("name"); 908 const char* ownerName = message->GetString("owner"); 909 // TODO: support arguments (as selectors) 910 911 ExternalEventSource* event = _FindEvent(ownerName, name); 912 if (event != NULL) { 913 // Evaluate all of its jobs 914 int32 count = event->CountListeners(); 915 for (int32 index = 0; index < count; index++) { 916 BaseJob* listener = event->ListenerAt(index); 917 Events::ResetStickyExternalEvent(listener->Event(), name); 918 } 919 } 920 } 921 922 _ForwardEventMessage(user, message); 923 } 924 925 926 uid_t 927 LaunchDaemon::_GetUserID(BMessage* message) 928 { 929 uid_t user = (uid_t)message->GetInt32("user", -1); 930 if (user < 0) { 931 BMessage reply((uint32)B_BAD_VALUE); 932 message->SendReply(&reply); 933 } 934 return user; 935 } 936 937 938 void 939 LaunchDaemon::_ReadPaths(const BStringList& paths) 940 { 941 for (int32 i = 0; i < paths.CountStrings(); i++) { 942 BEntry entry(paths.StringAt(i)); 943 if (entry.InitCheck() != B_OK || !entry.Exists()) 944 continue; 945 946 _ReadDirectory(NULL, entry); 947 } 948 } 949 950 951 void 952 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry) 953 { 954 if (entry.IsDirectory()) 955 _ReadDirectory(context, entry); 956 else 957 _ReadFile(context, entry); 958 } 959 960 961 void 962 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry) 963 { 964 BDirectory directory(&directoryEntry); 965 966 BEntry entry; 967 while (directory.GetNextEntry(&entry) == B_OK) { 968 _ReadEntry(context, entry); 969 } 970 } 971 972 973 status_t 974 LaunchDaemon::_ReadFile(const char* context, BEntry& entry) 975 { 976 BPath path; 977 status_t status = path.SetTo(&entry); 978 if (status != B_OK) 979 return status; 980 981 SettingsParser parser; 982 BMessage message; 983 status = parser.ParseFile(path.Path(), message); 984 if (status == B_OK) { 985 TRACE("launch_daemon: read file %s\n", path.Path()); 986 _AddJobs(NULL, message); 987 _AddTargets(message); 988 _AddRunTargets(message); 989 } 990 991 return status; 992 } 993 994 995 void 996 LaunchDaemon::_AddJobs(Target* target, BMessage& message) 997 { 998 BMessage job; 999 for (int32 index = 0; message.FindMessage("service", index, 1000 &job) == B_OK; index++) { 1001 _AddJob(target, true, job); 1002 } 1003 1004 for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK; 1005 index++) { 1006 _AddJob(target, false, job); 1007 } 1008 } 1009 1010 1011 void 1012 LaunchDaemon::_AddTargets(BMessage& message) 1013 { 1014 BMessage targetMessage; 1015 for (int32 index = 0; message.FindMessage("target", index, 1016 &targetMessage) == B_OK; index++) { 1017 const char* name = targetMessage.GetString("name"); 1018 if (name == NULL) { 1019 // TODO: log error 1020 debug_printf("Target has no name, ignoring it!\n"); 1021 continue; 1022 } 1023 1024 Target* target = FindTarget(name); 1025 if (target == NULL) { 1026 target = new Target(name); 1027 _AddTarget(target); 1028 } else if (targetMessage.GetBool("reset")) { 1029 // Remove all jobs from this target 1030 for (JobMap::iterator iterator = fJobs.begin(); 1031 iterator != fJobs.end();) { 1032 Job* job = iterator->second; 1033 JobMap::iterator remove = iterator++; 1034 1035 if (job->Target() == target) { 1036 fJobs.erase(remove); 1037 delete job; 1038 } 1039 } 1040 } 1041 1042 _SetCondition(target, targetMessage); 1043 _SetEvent(target, targetMessage); 1044 _SetEnvironment(target, targetMessage); 1045 _AddJobs(target, targetMessage); 1046 1047 if (target->Event() != NULL) 1048 target->Event()->Register(*this); 1049 } 1050 } 1051 1052 1053 void 1054 LaunchDaemon::_AddRunTargets(BMessage& message) 1055 { 1056 BMessage runMessage; 1057 for (int32 index = 0; message.FindMessage("run", index, 1058 &runMessage) == B_OK; index++) { 1059 BMessage conditions; 1060 bool pass = true; 1061 if (runMessage.FindMessage("if", &conditions) == B_OK) { 1062 Condition* condition = Conditions::FromMessage(conditions); 1063 if (condition != NULL) { 1064 pass = condition->Test(*this); 1065 debug_printf("Test: %s -> %d\n", condition->ToString().String(), 1066 pass); 1067 delete condition; 1068 } else 1069 debug_printf("Could not parse condition!\n"); 1070 } 1071 1072 if (pass) { 1073 _AddRunTargets(runMessage, NULL); 1074 _AddRunTargets(runMessage, "then"); 1075 } else { 1076 _AddRunTargets(runMessage, "else"); 1077 } 1078 } 1079 } 1080 1081 1082 void 1083 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name) 1084 { 1085 BMessage targets; 1086 if (name != NULL && message.FindMessage(name, &targets) != B_OK) 1087 return; 1088 1089 const char* target; 1090 for (int32 index = 0; targets.FindString("target", index, &target) == B_OK; 1091 index++) { 1092 fRunTargets.Add(target); 1093 } 1094 } 1095 1096 1097 void 1098 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) 1099 { 1100 BString name = message.GetString("name"); 1101 if (name.IsEmpty()) { 1102 // Invalid job description 1103 return; 1104 } 1105 name.ToLower(); 1106 1107 Job* job = FindJob(name); 1108 if (job == NULL) { 1109 TRACE(" add job \"%s\"\n", name.String()); 1110 1111 job = new (std::nothrow) Job(name); 1112 if (job == NULL) 1113 return; 1114 1115 job->SetService(service); 1116 job->SetCreateDefaultPort(service); 1117 job->SetTarget(target); 1118 } else 1119 TRACE(" amend job \"%s\"\n", name.String()); 1120 1121 if (message.HasBool("disabled")) 1122 job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled())); 1123 1124 if (message.HasBool("legacy")) 1125 job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); 1126 1127 _SetCondition(job, message); 1128 _SetEvent(job, message); 1129 _SetEnvironment(job, message); 1130 1131 BMessage portMessage; 1132 for (int32 index = 0; 1133 message.FindMessage("port", index, &portMessage) == B_OK; index++) { 1134 job->AddPort(portMessage); 1135 } 1136 1137 if (message.HasString("launch")) 1138 message.FindStrings("launch", &job->Arguments()); 1139 1140 const char* requirement; 1141 for (int32 index = 0; 1142 message.FindString("requires", index, &requirement) == B_OK; 1143 index++) { 1144 job->AddRequirement(requirement); 1145 } 1146 if (fInitTarget != NULL) 1147 job->AddRequirement(fInitTarget->Name()); 1148 1149 fJobs.insert(std::make_pair(job->Title(), job)); 1150 } 1151 1152 1153 /*! Initializes all jobs for the specified target (may be \c NULL). 1154 Jobs that cannot be initialized, and those that never will be due to 1155 conditions, will be removed from the list. 1156 */ 1157 void 1158 LaunchDaemon::_InitJobs(Target* target) 1159 { 1160 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) { 1161 Job* job = iterator->second; 1162 JobMap::iterator remove = iterator++; 1163 1164 if (job->Target() != target) 1165 continue; 1166 1167 status_t status = B_NO_INIT; 1168 if (job->IsEnabled()) { 1169 // Filter out jobs that have a constant and failing condition 1170 if (job->Condition() == NULL || !job->Condition()->IsConstant(*this) 1171 || job->Condition()->Test(*this)) { 1172 std::set<BString> dependencies; 1173 status = job->Init(*this, dependencies); 1174 if (status == B_OK && job->Event() != NULL) 1175 status = job->Event()->Register(*this); 1176 } 1177 } 1178 1179 if (status != B_OK) { 1180 if (status != B_NO_INIT) { 1181 // TODO: log error 1182 debug_printf("Init \"%s\" failed: %s\n", job->Name(), 1183 strerror(status)); 1184 } 1185 1186 // Remove jobs that won't be used later on 1187 fJobs.erase(remove); 1188 delete job; 1189 } 1190 } 1191 } 1192 1193 1194 /*! Adds all jobs for the specified target (may be \c NULL) to the launch 1195 queue, except those that are triggered by events that haven't been 1196 triggered yet. 1197 1198 Unless \a forceNow is true, the target is only launched if its events, 1199 if any, have been triggered already, and its conditions are met. 1200 */ 1201 void 1202 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow) 1203 { 1204 if (!forceNow && target != NULL && (!target->EventHasTriggered() 1205 || !target->CheckCondition(*this))) { 1206 return; 1207 } 1208 1209 if (target != NULL && !target->HasLaunched()) { 1210 target->SetLaunched(true); 1211 _InitJobs(target); 1212 } 1213 1214 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1215 iterator++) { 1216 Job* job = iterator->second; 1217 if (job->Target() == target) 1218 _LaunchJob(job); 1219 } 1220 } 1221 1222 1223 /*! Adds the specified \a job to the launch queue 1224 queue, except those that are triggered by events. 1225 1226 Unless \c FORCE_NOW is set, the target is only launched if its events, 1227 if any, have been triggered already. 1228 1229 Calling this method will trigger a demand event if \c TRIGGER_DEMAND has 1230 been set. 1231 */ 1232 void 1233 LaunchDaemon::_LaunchJob(Job* job, uint32 options) 1234 { 1235 if (job == NULL || job->IsLaunching() || job->IsRunning() 1236 || ((options & FORCE_NOW) == 0 1237 && (!job->EventHasTriggered() || !job->CheckCondition(*this) 1238 || ((options & TRIGGER_DEMAND) != 0 1239 && Events::TriggerDemand(job->Event()))))) { 1240 return; 1241 } 1242 1243 int32 count = job->Requirements().CountStrings(); 1244 for (int32 index = 0; index < count; index++) { 1245 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1246 if (requirement != NULL) { 1247 // TODO: For jobs that have their communication channels set up, 1248 // we would not need to trigger demand at this point 1249 _LaunchJob(requirement, TRIGGER_DEMAND); 1250 } 1251 } 1252 1253 if (job->Target() != NULL) 1254 job->Target()->ResolveSourceFiles(); 1255 if (job->Event() != NULL) 1256 job->Event()->ResetTrigger(); 1257 1258 job->SetLaunching(true); 1259 fJobQueue.AddJob(job); 1260 } 1261 1262 1263 void 1264 LaunchDaemon::_AddTarget(Target* target) 1265 { 1266 fTargets.insert(std::make_pair(target->Title(), target)); 1267 } 1268 1269 1270 void 1271 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message) 1272 { 1273 Condition* condition = job->Condition(); 1274 bool updated = false; 1275 1276 BMessage conditions; 1277 if (message.FindMessage("if", &conditions) == B_OK) { 1278 condition = Conditions::FromMessage(conditions); 1279 updated = true; 1280 } 1281 1282 if (message.GetBool("no_safemode")) { 1283 condition = Conditions::AddNotSafeMode(condition); 1284 updated = true; 1285 } 1286 1287 if (updated) 1288 job->SetCondition(condition); 1289 } 1290 1291 1292 void 1293 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message) 1294 { 1295 Event* event = job->Event(); 1296 bool updated = false; 1297 1298 BMessage events; 1299 if (message.FindMessage("on", &events) == B_OK) { 1300 event = Events::FromMessage(this, events); 1301 updated = true; 1302 } 1303 1304 if (message.GetBool("on_demand")) { 1305 event = Events::AddOnDemand(this, event); 1306 updated = true; 1307 } 1308 1309 if (updated) { 1310 TRACE(" event: %s\n", event->ToString().String()); 1311 job->SetEvent(event); 1312 _ResolveExternalEvents(job); 1313 } 1314 } 1315 1316 1317 void 1318 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message) 1319 { 1320 BMessage environmentMessage; 1321 if (message.FindMessage("env", &environmentMessage) == B_OK) 1322 job->SetEnvironment(environmentMessage); 1323 } 1324 1325 1326 ExternalEventSource* 1327 LaunchDaemon::_FindEvent(const char* owner, const char* name) const 1328 { 1329 if (name == NULL) 1330 return NULL; 1331 1332 BString eventName = name; 1333 eventName.ToLower(); 1334 1335 EventMap::const_iterator found = fEvents.find(eventName); 1336 if (found != fEvents.end()) 1337 return found->second; 1338 1339 if (owner == NULL) 1340 return NULL; 1341 1342 eventName.Prepend("/"); 1343 eventName.Prepend(get_leaf(owner)); 1344 1345 found = fEvents.find(eventName); 1346 if (found != fEvents.end()) 1347 return found->second; 1348 1349 return NULL; 1350 } 1351 1352 1353 void 1354 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* event, 1355 const BString& name) 1356 { 1357 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1358 iterator++) { 1359 Job* job = iterator->second; 1360 if (Events::ResolveExternalEvent(job->Event(), name, event->Flags())) 1361 event->AddListener(job); 1362 } 1363 } 1364 1365 1366 void 1367 LaunchDaemon::_ResolveExternalEvents(BaseJob* job) 1368 { 1369 if (job->Event() == NULL) 1370 return; 1371 1372 for (EventMap::iterator iterator = fEvents.begin(); 1373 iterator != fEvents.end(); iterator++) { 1374 ExternalEventSource* event = iterator->second; 1375 if (Events::ResolveExternalEvent(job->Event(), event->Name(), 1376 event->Flags())) 1377 event->AddListener(job); 1378 } 1379 } 1380 1381 1382 void 1383 LaunchDaemon::_GetTargetInfo(Target* target, BMessage& info) 1384 { 1385 info.SetString("name", target->Name()); 1386 } 1387 1388 1389 void 1390 LaunchDaemon::_GetJobInfo(Job* job, BMessage& info) 1391 { 1392 info.SetString("name", job->Name()); 1393 info.SetInt32("team", job->Team()); 1394 info.SetBool("running", job->IsRunning()); 1395 info.SetBool("launched", job->IsLaunched()); 1396 1397 PortMap::const_iterator iterator = job->Ports().begin(); 1398 for (; iterator != job->Ports().end(); iterator++) 1399 info.AddMessage("port", &iterator->second); 1400 } 1401 1402 1403 void 1404 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message) 1405 { 1406 if (fUserMode) 1407 return; 1408 1409 // Forward event to user launch_daemon(s) 1410 if (user == 0) { 1411 for (SessionMap::iterator iterator = fSessions.begin(); 1412 iterator != fSessions.end(); iterator++) { 1413 Session* session = iterator->second; 1414 session->Daemon().SendMessage(message); 1415 // ignore reply 1416 } 1417 } else { 1418 Session* session = FindSession(user); 1419 if (session != NULL) 1420 session->Daemon().SendMessage(message); 1421 } 1422 } 1423 1424 1425 status_t 1426 LaunchDaemon::_StartSession(const char* login) 1427 { 1428 // TODO: enable user/group code 1429 // The launch_daemon currently cannot talk to the registrar, though 1430 1431 struct passwd* passwd = getpwnam(login); 1432 if (passwd == NULL) 1433 return B_NAME_NOT_FOUND; 1434 if (strcmp(passwd->pw_name, login) != 0) 1435 return B_NAME_NOT_FOUND; 1436 1437 // Check if there is a user session running already 1438 uid_t user = passwd->pw_uid; 1439 gid_t group = passwd->pw_gid; 1440 1441 Unlock(); 1442 1443 if (fork() == 0) { 1444 if (setsid() < 0) 1445 exit(EXIT_FAILURE); 1446 1447 if (initgroups(login, group) == -1) 1448 exit(EXIT_FAILURE); 1449 if (setgid(group) != 0) 1450 exit(EXIT_FAILURE); 1451 if (setuid(user) != 0) 1452 exit(EXIT_FAILURE); 1453 1454 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') { 1455 setenv("HOME", passwd->pw_dir, true); 1456 1457 if (chdir(passwd->pw_dir) != 0) { 1458 debug_printf("Could not switch to home dir %s: %s\n", 1459 passwd->pw_dir, strerror(errno)); 1460 } 1461 } 1462 1463 // TODO: This leaks the parent application 1464 be_app = NULL; 1465 1466 // TODO: take over system jobs, and reserve their names 1467 status_t status; 1468 LaunchDaemon* daemon = new LaunchDaemon(true, fEvents, status); 1469 if (status == B_OK) 1470 daemon->Run(); 1471 1472 delete daemon; 1473 exit(EXIT_SUCCESS); 1474 } 1475 Lock(); 1476 return B_OK; 1477 } 1478 1479 1480 void 1481 LaunchDaemon::_RetrieveKernelOptions() 1482 { 1483 char buffer[32]; 1484 size_t size = sizeof(buffer); 1485 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer, 1486 &size); 1487 if (status == B_OK) { 1488 fSafeMode = !strncasecmp(buffer, "true", size) 1489 || !strncasecmp(buffer, "yes", size) 1490 || !strncasecmp(buffer, "on", size) 1491 || !strncasecmp(buffer, "enabled", size); 1492 } else 1493 fSafeMode = false; 1494 } 1495 1496 1497 void 1498 LaunchDaemon::_SetupEnvironment() 1499 { 1500 // Determine safemode kernel option 1501 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true); 1502 1503 // Default locale settings 1504 setenv("LC_TYPE", "en_US.UTF-8", true); 1505 } 1506 1507 1508 /*! Basic system initialization that must happen before any jobs are launched. 1509 */ 1510 void 1511 LaunchDaemon::_InitSystem() 1512 { 1513 _AddInitJob(new InitRealTimeClockJob()); 1514 _AddInitJob(new InitSharedMemoryDirectoryJob()); 1515 _AddInitJob(new InitTemporaryDirectoryJob()); 1516 1517 fJobQueue.AddJob(fInitTarget); 1518 } 1519 1520 1521 void 1522 LaunchDaemon::_AddInitJob(BJob* job) 1523 { 1524 fInitTarget->AddDependency(job); 1525 fJobQueue.AddJob(job); 1526 } 1527 1528 1529 // #pragma mark - 1530 1531 1532 static void 1533 open_stdio(int targetFD, int openMode) 1534 { 1535 int fd = open("/dev/null", openMode); 1536 if (fd != targetFD) { 1537 dup2(fd, targetFD); 1538 close(fd); 1539 } 1540 } 1541 1542 1543 int 1544 main() 1545 { 1546 // Make stdin/out/err available 1547 open_stdio(STDIN_FILENO, O_RDONLY); 1548 open_stdio(STDOUT_FILENO, O_WRONLY); 1549 dup2(STDOUT_FILENO, STDERR_FILENO); 1550 1551 EventMap events; 1552 status_t status; 1553 LaunchDaemon* daemon = new LaunchDaemon(false, events, status); 1554 if (status == B_OK) 1555 daemon->Run(); 1556 1557 delete daemon; 1558 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 1559 } 1560