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