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