1 /* 2 * Copyright 2015-2018, 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 <spawn.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <unistd.h> 18 19 #include <Directory.h> 20 #include <driver_settings.h> 21 #include <Entry.h> 22 #include <File.h> 23 #include <ObjectList.h> 24 #include <Path.h> 25 #include <PathFinder.h> 26 #include <Server.h> 27 28 #include <AppMisc.h> 29 #include <LaunchDaemonDefs.h> 30 #include <LaunchRosterPrivate.h> 31 #include <locks.h> 32 #include <MessengerPrivate.h> 33 #include <RosterPrivate.h> 34 #include <syscalls.h> 35 #include <system_info.h> 36 37 #include "multiuser_utils.h" 38 39 #include "Conditions.h" 40 #include "Events.h" 41 #include "InitRealTimeClockJob.h" 42 #include "InitSharedMemoryDirectoryJob.h" 43 #include "InitTemporaryDirectoryJob.h" 44 #include "Job.h" 45 #include "Log.h" 46 #include "SettingsParser.h" 47 #include "Target.h" 48 #include "Utility.h" 49 #include "Worker.h" 50 51 52 #ifdef DEBUG 53 # define TRACE(x, ...) debug_printf(x, __VA_ARGS__) 54 #else 55 # define TRACE(x, ...) ; 56 #endif 57 58 59 using namespace ::BPrivate; 60 using namespace BSupportKit; 61 using BSupportKit::BPrivate::JobQueue; 62 63 64 #ifndef TEST_MODE 65 static const char* kLaunchDirectory = "launch"; 66 static const char* kUserLaunchDirectory = "user_launch"; 67 #endif 68 69 70 enum launch_options { 71 FORCE_NOW = 0x01, 72 TRIGGER_DEMAND = 0x02 73 }; 74 75 76 class Session { 77 public: 78 Session(uid_t user, const BMessenger& target); 79 80 uid_t User() const 81 { return fUser; } 82 const BMessenger& Daemon() const 83 { return fDaemon; } 84 85 private: 86 uid_t fUser; 87 BMessenger fDaemon; 88 }; 89 90 91 /*! This class is the connection between the external events that are part of 92 a job, and the external event source. 93 94 There is one object per registered event source, and it keeps all jobs that 95 reference the event as listeners. If the event source triggers the event, 96 the object will be used to trigger the jobs. 97 */ 98 class ExternalEventSource { 99 public: 100 ExternalEventSource(BMessenger& source, 101 const char* ownerName, 102 const char* name, uint32 flags); 103 ~ExternalEventSource(); 104 105 const BMessenger& Source() const 106 { return fSource; } 107 108 const char* Name() const; 109 const char* OwnerName() const; 110 uint32 Flags() const 111 { return fFlags; } 112 113 void Trigger(); 114 bool StickyTriggered() const 115 { return fStickyTriggered; } 116 void ResetSticky(); 117 118 status_t AddDestination(Event* event); 119 void RemoveDestination(Event* event); 120 121 private: 122 BMessenger fSource; 123 BString fName, fOwnerName; 124 uint32 fFlags; 125 BObjectList<Event> fDestinations; 126 bool fStickyTriggered; 127 }; 128 129 130 typedef std::map<BString, Job*> JobMap; 131 typedef std::map<uid_t, Session*> SessionMap; 132 typedef std::map<BString, Target*> TargetMap; 133 typedef std::map<BString, ExternalEventSource*> EventMap; 134 typedef std::map<team_id, Job*> TeamMap; 135 136 137 class LaunchDaemon : public BServer, public Finder, public ConditionContext, 138 public EventRegistrator, public TeamListener { 139 public: 140 LaunchDaemon(bool userMode, status_t& error); 141 virtual ~LaunchDaemon(); 142 143 virtual Job* FindJob(const char* name) const; 144 virtual Target* FindTarget(const char* name) const; 145 Session* FindSession(uid_t user) const; 146 147 // ConditionContext 148 virtual bool IsSafeMode() const; 149 virtual bool BootVolumeIsReadOnly() const; 150 151 // EventRegistrator 152 virtual status_t RegisterExternalEvent(Event* event, 153 const char* name, 154 const BStringList& arguments); 155 virtual void UnregisterExternalEvent(Event* event, 156 const char* name); 157 158 // TeamListener 159 virtual void TeamLaunched(Job* job, status_t status); 160 161 virtual void ReadyToRun(); 162 virtual void MessageReceived(BMessage* message); 163 164 private: 165 void _HandleGetLaunchData(BMessage* message); 166 void _HandleLaunchTarget(BMessage* message); 167 void _HandleStopLaunchTarget(BMessage* message); 168 void _HandleLaunchJob(BMessage* message); 169 void _HandleEnableLaunchJob(BMessage* message); 170 void _HandleStopLaunchJob(BMessage* message); 171 void _HandleLaunchSession(BMessage* message); 172 void _HandleRegisterSessionDaemon(BMessage* message); 173 void _HandleRegisterLaunchEvent(BMessage* message); 174 void _HandleUnregisterLaunchEvent(BMessage* message); 175 void _HandleNotifyLaunchEvent(BMessage* message); 176 void _HandleResetStickyLaunchEvent( 177 BMessage* message); 178 void _HandleGetLaunchTargets(BMessage* message); 179 void _HandleGetLaunchTargetInfo(BMessage* message); 180 void _HandleGetLaunchJobs(BMessage* message); 181 void _HandleGetLaunchJobInfo(BMessage* message); 182 void _HandleGetLaunchLog(BMessage* message); 183 uid_t _GetUserID(BMessage* message); 184 185 void _ReadPaths(const BStringList& paths); 186 void _ReadEntry(const char* context, BEntry& entry); 187 void _ReadDirectory(const char* context, 188 BEntry& directory); 189 status_t _ReadFile(const char* context, BEntry& entry); 190 191 void _AddJobs(Target* target, BMessage& message); 192 void _AddTargets(BMessage& message); 193 void _AddRunTargets(BMessage& message); 194 void _AddRunTargets(BMessage& message, 195 const char* name); 196 void _AddJob(Target* target, bool service, 197 BMessage& message); 198 void _InitJobs(Target* target); 199 void _LaunchJobs(Target* target, 200 bool forceNow = false); 201 void _StopJobs(Target* target, bool force); 202 bool _CanLaunchJob(Job* job, uint32 options, 203 bool testOnly = false); 204 bool _CanLaunchJobRequirements(Job* job, 205 uint32 options); 206 bool _LaunchJob(Job* job, uint32 options = 0); 207 void _StopJob(Job* job, bool force); 208 void _AddTarget(Target* target); 209 void _SetCondition(BaseJob* job, 210 const BMessage& message); 211 void _SetEvent(BaseJob* job, 212 const BMessage& message); 213 void _SetEnvironment(BaseJob* job, 214 const BMessage& message); 215 216 ExternalEventSource* 217 _FindExternalEventSource(const char* owner, 218 const char* name) const; 219 void _ResolveExternalEvents( 220 ExternalEventSource* event, 221 const BString& name); 222 void _GetBaseJobInfo(BaseJob* job, BMessage& info); 223 void _ForwardEventMessage(uid_t user, 224 BMessage* message); 225 226 status_t _StartSession(const char* login); 227 228 void _RetrieveKernelOptions(); 229 void _SetupEnvironment(); 230 void _InitSystem(); 231 void _AddInitJob(BJob* job); 232 233 private: 234 Log fLog; 235 JobMap fJobs; 236 TargetMap fTargets; 237 BStringList fRunTargets; 238 EventMap fEvents; 239 JobQueue fJobQueue; 240 SessionMap fSessions; 241 MainWorker* fMainWorker; 242 Target* fInitTarget; 243 TeamMap fTeams; 244 mutex fTeamsLock; 245 bool fSafeMode; 246 bool fReadOnlyBootVolume; 247 bool fUserMode; 248 }; 249 250 251 static const char* 252 get_leaf(const char* signature) 253 { 254 if (signature == NULL) 255 return NULL; 256 257 const char* separator = strrchr(signature, '/'); 258 if (separator != NULL) 259 return separator + 1; 260 261 return signature; 262 } 263 264 265 // #pragma mark - 266 267 268 Session::Session(uid_t user, const BMessenger& daemon) 269 : 270 fUser(user), 271 fDaemon(daemon) 272 { 273 } 274 275 276 // #pragma mark - 277 278 279 ExternalEventSource::ExternalEventSource(BMessenger& source, 280 const char* ownerName, const char* name, uint32 flags) 281 : 282 fSource(source), 283 fName(name), 284 fOwnerName(ownerName), 285 fFlags(flags), 286 fDestinations(5), 287 fStickyTriggered(false) 288 { 289 } 290 291 292 ExternalEventSource::~ExternalEventSource() 293 { 294 } 295 296 297 const char* 298 ExternalEventSource::Name() const 299 { 300 return fName.String(); 301 } 302 303 304 const char* 305 ExternalEventSource::OwnerName() const 306 { 307 return fOwnerName.String(); 308 } 309 310 311 void 312 ExternalEventSource::Trigger() 313 { 314 for (int32 index = 0; index < fDestinations.CountItems(); index++) 315 Events::TriggerExternalEvent(fDestinations.ItemAt(index)); 316 317 if ((fFlags & B_STICKY_EVENT) != 0) 318 fStickyTriggered = true; 319 } 320 321 322 void 323 ExternalEventSource::ResetSticky() 324 { 325 if ((fFlags & B_STICKY_EVENT) != 0) 326 fStickyTriggered = false; 327 328 for (int32 index = 0; index < fDestinations.CountItems(); index++) 329 Events::ResetStickyExternalEvent(fDestinations.ItemAt(index)); 330 } 331 332 333 status_t 334 ExternalEventSource::AddDestination(Event* event) 335 { 336 if (fStickyTriggered) 337 Events::TriggerExternalEvent(event); 338 339 if (fDestinations.AddItem(event)) 340 return B_OK; 341 342 return B_NO_MEMORY; 343 } 344 345 346 void 347 ExternalEventSource::RemoveDestination(Event* event) 348 { 349 fDestinations.RemoveItem(event); 350 } 351 352 353 // #pragma mark - 354 355 356 LaunchDaemon::LaunchDaemon(bool userMode, status_t& error) 357 : 358 BServer(kLaunchDaemonSignature, NULL, 359 create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, 360 userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error), 361 fInitTarget(userMode ? NULL : new Target("init")), 362 #ifdef TEST_MODE 363 fUserMode(true) 364 #else 365 fUserMode(userMode) 366 #endif 367 { 368 mutex_init(&fTeamsLock, "teams lock"); 369 370 fMainWorker = new MainWorker(fJobQueue); 371 fMainWorker->Init(); 372 373 if (fInitTarget != NULL) 374 _AddTarget(fInitTarget); 375 376 // We may not be able to talk to the registrar 377 if (!fUserMode) 378 BRoster::Private().SetWithoutRegistrar(true); 379 } 380 381 382 LaunchDaemon::~LaunchDaemon() 383 { 384 } 385 386 387 Job* 388 LaunchDaemon::FindJob(const char* name) const 389 { 390 if (name == NULL) 391 return NULL; 392 393 JobMap::const_iterator found = fJobs.find(BString(name).ToLower()); 394 if (found != fJobs.end()) 395 return found->second; 396 397 return NULL; 398 } 399 400 401 Target* 402 LaunchDaemon::FindTarget(const char* name) const 403 { 404 if (name == NULL) 405 return NULL; 406 407 TargetMap::const_iterator found = fTargets.find(BString(name).ToLower()); 408 if (found != fTargets.end()) 409 return found->second; 410 411 return NULL; 412 } 413 414 415 Session* 416 LaunchDaemon::FindSession(uid_t user) const 417 { 418 SessionMap::const_iterator found = fSessions.find(user); 419 if (found != fSessions.end()) 420 return found->second; 421 422 return NULL; 423 } 424 425 426 bool 427 LaunchDaemon::IsSafeMode() const 428 { 429 return fSafeMode; 430 } 431 432 433 bool 434 LaunchDaemon::BootVolumeIsReadOnly() const 435 { 436 return fReadOnlyBootVolume; 437 } 438 439 440 status_t 441 LaunchDaemon::RegisterExternalEvent(Event* event, const char* name, 442 const BStringList& arguments) 443 { 444 status_t status = B_NAME_NOT_FOUND; 445 for (EventMap::iterator iterator = fEvents.begin(); 446 iterator != fEvents.end(); iterator++) { 447 ExternalEventSource* eventSource = iterator->second; 448 Event* externalEvent = Events::ResolveExternalEvent(event, 449 eventSource->Name(), eventSource->Flags()); 450 if (externalEvent != NULL) { 451 status = eventSource->AddDestination(event); 452 break; 453 } 454 } 455 456 TRACE("Register external event '%s': %" B_PRId32 "\n", name, status); 457 458 // Even if we failed to find a matching source, we do not want to return an error, 459 // as that will be propagated up the chain and prevent this job from being instantiated. 460 // Jobs will be re-scanned later for unregistered external events. 461 return B_OK; 462 } 463 464 465 void 466 LaunchDaemon::UnregisterExternalEvent(Event* event, const char* name) 467 { 468 for (EventMap::iterator iterator = fEvents.begin(); 469 iterator != fEvents.end(); iterator++) { 470 ExternalEventSource* eventSource = iterator->second; 471 Event* externalEvent = Events::ResolveExternalEvent(event, 472 eventSource->Name(), eventSource->Flags()); 473 if (externalEvent != NULL) { 474 eventSource->RemoveDestination(event); 475 break; 476 } 477 } 478 } 479 480 481 void 482 LaunchDaemon::TeamLaunched(Job* job, status_t status) 483 { 484 fLog.JobLaunched(job, status); 485 486 MutexLocker locker(fTeamsLock); 487 fTeams.insert(std::make_pair(job->Team(), job)); 488 } 489 490 491 void 492 LaunchDaemon::ReadyToRun() 493 { 494 _RetrieveKernelOptions(); 495 _SetupEnvironment(); 496 497 fReadOnlyBootVolume = Utility::IsReadOnlyVolume("/boot"); 498 if (fReadOnlyBootVolume) 499 Utility::BlockMedia("/boot", true); 500 501 if (fUserMode) { 502 #ifndef TEST_MODE 503 BLaunchRoster roster; 504 BLaunchRoster::Private(roster).RegisterSessionDaemon(this); 505 #endif // TEST_MODE 506 } else 507 _InitSystem(); 508 509 BStringList paths; 510 #ifdef TEST_MODE 511 paths.Add("/boot/home/test_launch"); 512 #else 513 if (fUserMode) { 514 // System-wide user specific jobs 515 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kUserLaunchDirectory, 516 B_FIND_PATHS_SYSTEM_ONLY, paths); 517 _ReadPaths(paths); 518 } 519 520 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory, 521 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 522 _ReadPaths(paths); 523 524 if (fUserMode) { 525 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, 526 kUserLaunchDirectory, B_FIND_PATHS_SYSTEM_ONLY, paths); 527 _ReadPaths(paths); 528 } 529 530 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory, 531 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 532 #endif // TEST_MODE 533 _ReadPaths(paths); 534 535 BMessenger target(this); 536 BMessenger::Private messengerPrivate(target); 537 port_id port = messengerPrivate.Port(); 538 int32 token = messengerPrivate.Token(); 539 __start_watching_system(-1, B_WATCH_SYSTEM_TEAM_DELETION, port, token); 540 541 _InitJobs(NULL); 542 _LaunchJobs(NULL); 543 544 // Launch run targets (ignores events) 545 for (int32 index = 0; index < fRunTargets.CountStrings(); index++) { 546 Target* target = FindTarget(fRunTargets.StringAt(index)); 547 if (target != NULL) 548 _LaunchJobs(target); 549 } 550 551 if (fUserMode) 552 be_roster->StartWatching(this, B_REQUEST_LAUNCHED); 553 } 554 555 556 void 557 LaunchDaemon::MessageReceived(BMessage* message) 558 { 559 switch (message->what) { 560 case B_SYSTEM_OBJECT_UPDATE: 561 { 562 int32 opcode = message->GetInt32("opcode", 0); 563 team_id team = (team_id)message->GetInt32("team", -1); 564 if (opcode != B_TEAM_DELETED || team < 0) 565 break; 566 567 MutexLocker locker(fTeamsLock); 568 569 TeamMap::iterator found = fTeams.find(team); 570 if (found != fTeams.end()) { 571 Job* job = found->second; 572 TRACE("Job %s ended!\n", job->Name()); 573 574 // Get the exit status, and pass it on to the log 575 status_t exitStatus = B_OK; 576 wait_for_thread(team, &exitStatus); 577 fLog.JobTerminated(job, exitStatus); 578 job->TeamDeleted(); 579 580 if (job->IsService()) { 581 bool inProgress = false; 582 BRoster roster; 583 BRoster::Private rosterPrivate(roster); 584 status_t status = rosterPrivate.IsShutDownInProgress( 585 &inProgress); 586 if (status != B_OK || !inProgress) { 587 // TODO: take restart throttle into account 588 _LaunchJob(job); 589 } 590 } 591 } 592 break; 593 } 594 case B_SOME_APP_LAUNCHED: 595 { 596 team_id team = (team_id)message->GetInt32("be:team", -1); 597 Job* job = NULL; 598 599 MutexLocker locker(fTeamsLock); 600 601 TeamMap::iterator found = fTeams.find(team); 602 if (found != fTeams.end()) { 603 job = found->second; 604 locker.Unlock(); 605 } else { 606 locker.Unlock(); 607 608 // Find job by name instead 609 const char* signature = message->GetString("be:signature"); 610 job = FindJob(get_leaf(signature)); 611 if (job != NULL) { 612 TRACE("Updated default port of untracked team %d, %s\n", 613 (int)team, signature); 614 } 615 } 616 617 if (job != NULL) { 618 // Update port info 619 app_info info; 620 status_t status = be_roster->GetRunningAppInfo(team, &info); 621 if (status == B_OK && info.port != job->DefaultPort()) { 622 TRACE("Update default port for %s to %d\n", job->Name(), 623 (int)info.port); 624 job->SetDefaultPort(info.port); 625 } 626 } 627 break; 628 } 629 630 case B_GET_LAUNCH_DATA: 631 _HandleGetLaunchData(message); 632 break; 633 634 case B_LAUNCH_TARGET: 635 _HandleLaunchTarget(message); 636 break; 637 case B_STOP_LAUNCH_TARGET: 638 _HandleStopLaunchTarget(message); 639 break; 640 case B_LAUNCH_JOB: 641 _HandleLaunchJob(message); 642 break; 643 case B_ENABLE_LAUNCH_JOB: 644 _HandleEnableLaunchJob(message); 645 break; 646 case B_STOP_LAUNCH_JOB: 647 _HandleStopLaunchJob(message); 648 break; 649 650 case B_LAUNCH_SESSION: 651 _HandleLaunchSession(message); 652 break; 653 case B_REGISTER_SESSION_DAEMON: 654 _HandleRegisterSessionDaemon(message); 655 break; 656 657 case B_REGISTER_LAUNCH_EVENT: 658 _HandleRegisterLaunchEvent(message); 659 break; 660 case B_UNREGISTER_LAUNCH_EVENT: 661 _HandleUnregisterLaunchEvent(message); 662 break; 663 case B_NOTIFY_LAUNCH_EVENT: 664 _HandleNotifyLaunchEvent(message); 665 break; 666 case B_RESET_STICKY_LAUNCH_EVENT: 667 _HandleResetStickyLaunchEvent(message); 668 break; 669 670 case B_GET_LAUNCH_TARGETS: 671 _HandleGetLaunchTargets(message); 672 break; 673 case B_GET_LAUNCH_TARGET_INFO: 674 _HandleGetLaunchTargetInfo(message); 675 break; 676 case B_GET_LAUNCH_JOBS: 677 _HandleGetLaunchJobs(message); 678 break; 679 case B_GET_LAUNCH_JOB_INFO: 680 _HandleGetLaunchJobInfo(message); 681 break; 682 683 case B_GET_LAUNCH_LOG: 684 _HandleGetLaunchLog(message); 685 break; 686 687 case kMsgEventTriggered: 688 { 689 // An internal event has been triggered. 690 // Check if its job(s) can be launched now. 691 const char* name = message->GetString("owner"); 692 if (name == NULL) 693 break; 694 695 Event* event = (Event*)message->GetPointer("event"); 696 697 Job* job = FindJob(name); 698 if (job != NULL) { 699 fLog.EventTriggered(job, event); 700 _LaunchJob(job); 701 break; 702 } 703 704 Target* target = FindTarget(name); 705 if (target != NULL) { 706 fLog.EventTriggered(target, event); 707 _LaunchJobs(target); 708 break; 709 } 710 break; 711 } 712 713 default: 714 BServer::MessageReceived(message); 715 break; 716 } 717 } 718 719 720 void 721 LaunchDaemon::_HandleGetLaunchData(BMessage* message) 722 { 723 uid_t user = _GetUserID(message); 724 if (user < 0) 725 return; 726 727 BMessage reply((uint32)B_OK); 728 bool launchJob = true; 729 730 Job* job = FindJob(get_leaf(message->GetString("name"))); 731 if (job == NULL) { 732 Session* session = FindSession(user); 733 if (session != NULL) { 734 // Forward request to user launch_daemon 735 if (session->Daemon().SendMessage(message) == B_OK) 736 return; 737 } 738 reply.what = B_NAME_NOT_FOUND; 739 } else if (job->IsService() && !job->IsLaunched()) { 740 if (job->InitCheck() == B_NO_INIT || !job->CheckCondition(*this)) { 741 // The job exists, but cannot be started yet, as its 742 // conditions are not met; don't make it available yet 743 // TODO: we may not want to initialize jobs with conditions 744 // that aren't met yet 745 reply.what = B_NO_INIT; 746 } else if (job->Event() != NULL) { 747 if (!Events::TriggerDemand(job->Event())) { 748 // The job is not triggered by demand; we cannot start it now 749 reply.what = B_NO_INIT; 750 } else { 751 // The job has already been triggered, don't launch it again 752 launchJob = false; 753 } 754 } 755 } else 756 launchJob = false; 757 758 bool ownsMessage = false; 759 if (reply.what == B_OK) { 760 // Launch the job if it hasn't been launched already 761 if (launchJob) 762 _LaunchJob(job, TRIGGER_DEMAND); 763 764 DetachCurrentMessage(); 765 status_t result = job->HandleGetLaunchData(message); 766 if (result == B_OK) { 767 // Replying is delegated to the job. 768 return; 769 } 770 771 ownsMessage = true; 772 reply.what = result; 773 } 774 775 message->SendReply(&reply); 776 if (ownsMessage) 777 delete message; 778 } 779 780 781 void 782 LaunchDaemon::_HandleLaunchTarget(BMessage* message) 783 { 784 uid_t user = _GetUserID(message); 785 if (user < 0) 786 return; 787 788 const char* name = message->GetString("target"); 789 const char* baseName = message->GetString("base target"); 790 791 Target* target = FindTarget(name); 792 if (target == NULL && baseName != NULL) { 793 Target* baseTarget = FindTarget(baseName); 794 if (baseTarget != NULL) { 795 target = new Target(name); 796 797 // Copy all jobs with the base target into the new target 798 for (JobMap::iterator iterator = fJobs.begin(); 799 iterator != fJobs.end();) { 800 Job* job = iterator->second; 801 iterator++; 802 803 if (job->Target() == baseTarget) { 804 Job* copy = new Job(*job); 805 copy->SetTarget(target); 806 807 fJobs.insert(std::make_pair(copy->Name(), copy)); 808 } 809 } 810 } 811 } 812 if (target == NULL) { 813 Session* session = FindSession(user); 814 if (session != NULL) { 815 // Forward request to user launch_daemon 816 if (session->Daemon().SendMessage(message) == B_OK) 817 return; 818 } 819 820 BMessage reply(B_NAME_NOT_FOUND); 821 message->SendReply(&reply); 822 return; 823 } 824 825 BMessage data; 826 if (message->FindMessage("data", &data) == B_OK) 827 target->AddData(data.GetString("name"), data); 828 829 _LaunchJobs(target); 830 831 BMessage reply((uint32)B_OK); 832 message->SendReply(&reply); 833 } 834 835 836 void 837 LaunchDaemon::_HandleStopLaunchTarget(BMessage* message) 838 { 839 uid_t user = _GetUserID(message); 840 if (user < 0) 841 return; 842 843 const char* name = message->GetString("target"); 844 845 Target* target = FindTarget(name); 846 if (target == NULL) { 847 Session* session = FindSession(user); 848 if (session != NULL) { 849 // Forward request to user launch_daemon 850 if (session->Daemon().SendMessage(message) == B_OK) 851 return; 852 } 853 854 BMessage reply(B_NAME_NOT_FOUND); 855 message->SendReply(&reply); 856 return; 857 } 858 859 BMessage data; 860 if (message->FindMessage("data", &data) == B_OK) 861 target->AddData(data.GetString("name"), data); 862 863 bool force = message->GetBool("force"); 864 fLog.JobStopped(target, force); 865 _StopJobs(target, force); 866 867 BMessage reply((uint32)B_OK); 868 message->SendReply(&reply); 869 } 870 871 872 void 873 LaunchDaemon::_HandleLaunchJob(BMessage* message) 874 { 875 uid_t user = _GetUserID(message); 876 if (user < 0) 877 return; 878 879 const char* name = message->GetString("name"); 880 881 Job* job = FindJob(name); 882 if (job == NULL) { 883 Session* session = FindSession(user); 884 if (session != NULL) { 885 // Forward request to user launch_daemon 886 if (session->Daemon().SendMessage(message) == B_OK) 887 return; 888 } 889 890 BMessage reply(B_NAME_NOT_FOUND); 891 message->SendReply(&reply); 892 return; 893 } 894 895 job->SetEnabled(true); 896 _LaunchJob(job, FORCE_NOW); 897 898 BMessage reply((uint32)B_OK); 899 message->SendReply(&reply); 900 } 901 902 903 void 904 LaunchDaemon::_HandleEnableLaunchJob(BMessage* message) 905 { 906 uid_t user = _GetUserID(message); 907 if (user < 0) 908 return; 909 910 const char* name = message->GetString("name"); 911 bool enable = message->GetBool("enable"); 912 913 Job* job = FindJob(name); 914 if (job == NULL) { 915 Session* session = FindSession(user); 916 if (session != NULL) { 917 // Forward request to user launch_daemon 918 if (session->Daemon().SendMessage(message) == B_OK) 919 return; 920 } 921 922 BMessage reply(B_NAME_NOT_FOUND); 923 message->SendReply(&reply); 924 return; 925 } 926 927 job->SetEnabled(enable); 928 fLog.JobEnabled(job, enable); 929 930 BMessage reply((uint32)B_OK); 931 message->SendReply(&reply); 932 } 933 934 935 void 936 LaunchDaemon::_HandleStopLaunchJob(BMessage* message) 937 { 938 uid_t user = _GetUserID(message); 939 if (user < 0) 940 return; 941 942 const char* name = message->GetString("name"); 943 944 Job* job = FindJob(name); 945 if (job == NULL) { 946 Session* session = FindSession(user); 947 if (session != NULL) { 948 // Forward request to user launch_daemon 949 if (session->Daemon().SendMessage(message) == B_OK) 950 return; 951 } 952 953 BMessage reply(B_NAME_NOT_FOUND); 954 message->SendReply(&reply); 955 return; 956 } 957 958 bool force = message->GetBool("force"); 959 fLog.JobStopped(job, force); 960 _StopJob(job, force); 961 962 BMessage reply((uint32)B_OK); 963 message->SendReply(&reply); 964 } 965 966 967 void 968 LaunchDaemon::_HandleLaunchSession(BMessage* message) 969 { 970 uid_t user = _GetUserID(message); 971 if (user < 0) 972 return; 973 974 status_t status = B_OK; 975 const char* login = message->GetString("login"); 976 if (login == NULL) 977 status = B_BAD_VALUE; 978 if (status == B_OK && user != 0) { 979 // Only the root user can start sessions 980 // TODO: we'd actually need to know the uid of the sender 981 status = B_PERMISSION_DENIED; 982 } 983 if (status == B_OK) 984 status = _StartSession(login); 985 986 BMessage reply((uint32)status); 987 message->SendReply(&reply); 988 } 989 990 991 void 992 LaunchDaemon::_HandleRegisterSessionDaemon(BMessage* message) 993 { 994 uid_t user = _GetUserID(message); 995 if (user < 0) 996 return; 997 998 status_t status = B_OK; 999 1000 BMessenger target; 1001 if (message->FindMessenger("daemon", &target) != B_OK) 1002 status = B_BAD_VALUE; 1003 1004 if (status == B_OK) { 1005 Session* session = new (std::nothrow) Session(user, target); 1006 if (session != NULL) 1007 fSessions.insert(std::make_pair(user, session)); 1008 else 1009 status = B_NO_MEMORY; 1010 1011 // Send registration messages for all already-known events. 1012 for (EventMap::iterator iterator = fEvents.begin(); iterator != fEvents.end(); 1013 iterator++) { 1014 ExternalEventSource* eventSource = iterator->second; 1015 if (eventSource->Name() != iterator->first) 1016 continue; // skip alternative event names 1017 1018 BMessage message(B_REGISTER_LAUNCH_EVENT); 1019 message.AddInt32("user", 0); 1020 message.AddString("name", eventSource->Name()); 1021 message.AddString("owner", eventSource->OwnerName()); 1022 message.AddUInt32("flags", eventSource->Flags()); 1023 message.AddMessenger("source", eventSource->Source()); 1024 target.SendMessage(&message); 1025 1026 if (eventSource->StickyTriggered()) { 1027 message.what = B_NOTIFY_LAUNCH_EVENT; 1028 target.SendMessage(&message); 1029 } 1030 } 1031 } 1032 1033 BMessage reply((uint32)status); 1034 message->SendReply(&reply); 1035 } 1036 1037 1038 void 1039 LaunchDaemon::_HandleRegisterLaunchEvent(BMessage* message) 1040 { 1041 uid_t user = _GetUserID(message); 1042 if (user < 0) 1043 return; 1044 1045 if (user == 0 || fUserMode) { 1046 status_t status = B_OK; 1047 1048 const char* name = message->GetString("name"); 1049 const char* ownerName = message->GetString("owner"); 1050 uint32 flags = message->GetUInt32("flags", 0); 1051 BMessenger source; 1052 if (name != NULL && ownerName != NULL 1053 && message->FindMessenger("source", &source) == B_OK) { 1054 // Register event 1055 ownerName = get_leaf(ownerName); 1056 1057 ExternalEventSource* event = new (std::nothrow) 1058 ExternalEventSource(source, ownerName, name, flags); 1059 if (event != NULL) { 1060 // Use short name, and fully qualified name 1061 BString eventName = name; 1062 fEvents.insert(std::make_pair(eventName, event)); 1063 _ResolveExternalEvents(event, eventName); 1064 1065 eventName.Prepend("/"); 1066 eventName.Prepend(ownerName); 1067 fEvents.insert(std::make_pair(eventName, event)); 1068 _ResolveExternalEvents(event, eventName); 1069 1070 fLog.ExternalEventRegistered(name); 1071 } else 1072 status = B_NO_MEMORY; 1073 } else 1074 status = B_BAD_VALUE; 1075 1076 BMessage reply((uint32)status); 1077 message->SendReply(&reply); 1078 } 1079 1080 _ForwardEventMessage(user, message); 1081 } 1082 1083 1084 void 1085 LaunchDaemon::_HandleUnregisterLaunchEvent(BMessage* message) 1086 { 1087 uid_t user = _GetUserID(message); 1088 if (user < 0) 1089 return; 1090 1091 if (user == 0 || fUserMode) { 1092 status_t status = B_OK; 1093 1094 const char* name = message->GetString("name"); 1095 const char* ownerName = message->GetString("owner"); 1096 BMessenger source; 1097 if (name != NULL && ownerName != NULL 1098 && message->FindMessenger("source", &source) == B_OK) { 1099 // Unregister short and fully qualified event name 1100 ownerName = get_leaf(ownerName); 1101 1102 BString eventName = name; 1103 fEvents.erase(eventName); 1104 1105 eventName.Prepend("/"); 1106 eventName.Prepend(ownerName); 1107 fEvents.erase(eventName); 1108 1109 fLog.ExternalEventRegistered(name); 1110 } else 1111 status = B_BAD_VALUE; 1112 1113 BMessage reply((uint32)status); 1114 message->SendReply(&reply); 1115 } 1116 1117 _ForwardEventMessage(user, message); 1118 } 1119 1120 1121 void 1122 LaunchDaemon::_HandleNotifyLaunchEvent(BMessage* message) 1123 { 1124 uid_t user = _GetUserID(message); 1125 if (user < 0) 1126 return; 1127 1128 if (user == 0 || fUserMode) { 1129 // Trigger events 1130 const char* name = message->GetString("name"); 1131 const char* ownerName = message->GetString("owner"); 1132 // TODO: support arguments (as selectors) 1133 1134 ExternalEventSource* event = _FindExternalEventSource(ownerName, name); 1135 if (event != NULL) { 1136 fLog.ExternalEventTriggered(name); 1137 event->Trigger(); 1138 } 1139 } 1140 1141 _ForwardEventMessage(user, message); 1142 } 1143 1144 1145 void 1146 LaunchDaemon::_HandleResetStickyLaunchEvent(BMessage* message) 1147 { 1148 uid_t user = _GetUserID(message); 1149 if (user < 0) 1150 return; 1151 1152 if (user == 0 || fUserMode) { 1153 // Reset sticky events 1154 const char* name = message->GetString("name"); 1155 const char* ownerName = message->GetString("owner"); 1156 // TODO: support arguments (as selectors) 1157 1158 ExternalEventSource* eventSource = _FindExternalEventSource(ownerName, name); 1159 if (eventSource != NULL) 1160 eventSource->ResetSticky(); 1161 } 1162 1163 _ForwardEventMessage(user, message); 1164 } 1165 1166 1167 void 1168 LaunchDaemon::_HandleGetLaunchTargets(BMessage* message) 1169 { 1170 uid_t user = _GetUserID(message); 1171 if (user < 0) 1172 return; 1173 1174 BMessage reply; 1175 status_t status = B_OK; 1176 1177 if (!fUserMode) { 1178 // Request the data from the user's daemon, too 1179 Session* session = FindSession(user); 1180 if (session != NULL) { 1181 BMessage request(B_GET_LAUNCH_TARGETS); 1182 status = request.AddInt32("user", 0); 1183 if (status == B_OK) { 1184 status = session->Daemon().SendMessage(&request, 1185 &reply); 1186 } 1187 if (status == B_OK) 1188 status = reply.what; 1189 } else 1190 status = B_NAME_NOT_FOUND; 1191 } 1192 1193 if (status == B_OK) { 1194 TargetMap::const_iterator iterator = fTargets.begin(); 1195 for (; iterator != fTargets.end(); iterator++) 1196 reply.AddString("target", iterator->first); 1197 } 1198 1199 reply.what = status; 1200 message->SendReply(&reply); 1201 } 1202 1203 1204 void 1205 LaunchDaemon::_HandleGetLaunchTargetInfo(BMessage* message) 1206 { 1207 uid_t user = _GetUserID(message); 1208 if (user < 0) 1209 return; 1210 1211 const char* name = message->GetString("name"); 1212 Target* target = FindTarget(name); 1213 if (target == NULL && !fUserMode) { 1214 _ForwardEventMessage(user, message); 1215 return; 1216 } 1217 1218 BMessage info(uint32(target != NULL ? B_OK : B_NAME_NOT_FOUND)); 1219 if (target != NULL) { 1220 _GetBaseJobInfo(target, info); 1221 1222 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1223 iterator++) { 1224 Job* job = iterator->second; 1225 if (job->Target() == target) 1226 info.AddString("job", job->Name()); 1227 } 1228 } 1229 message->SendReply(&info); 1230 } 1231 1232 1233 void 1234 LaunchDaemon::_HandleGetLaunchJobs(BMessage* message) 1235 { 1236 uid_t user = _GetUserID(message); 1237 if (user < 0) 1238 return; 1239 1240 const char* targetName = message->GetString("target"); 1241 1242 BMessage reply; 1243 status_t status = B_OK; 1244 1245 if (!fUserMode) { 1246 // Request the data from the user's daemon, too 1247 Session* session = FindSession(user); 1248 if (session != NULL) { 1249 BMessage request(B_GET_LAUNCH_JOBS); 1250 status = request.AddInt32("user", 0); 1251 if (status == B_OK && targetName != NULL) 1252 status = request.AddString("target", targetName); 1253 if (status == B_OK) { 1254 status = session->Daemon().SendMessage(&request, 1255 &reply); 1256 } 1257 if (status == B_OK) 1258 status = reply.what; 1259 } else 1260 status = B_NAME_NOT_FOUND; 1261 } 1262 1263 if (status == B_OK) { 1264 JobMap::const_iterator iterator = fJobs.begin(); 1265 for (; iterator != fJobs.end(); iterator++) { 1266 Job* job = iterator->second; 1267 if (targetName != NULL && (job->Target() == NULL 1268 || job->Target()->Title() != targetName)) { 1269 continue; 1270 } 1271 reply.AddString("job", iterator->first); 1272 } 1273 } 1274 1275 reply.what = status; 1276 message->SendReply(&reply); 1277 } 1278 1279 1280 void 1281 LaunchDaemon::_HandleGetLaunchJobInfo(BMessage* message) 1282 { 1283 uid_t user = _GetUserID(message); 1284 if (user < 0) 1285 return; 1286 1287 const char* name = message->GetString("name"); 1288 Job* job = FindJob(name); 1289 if (job == NULL && !fUserMode) { 1290 _ForwardEventMessage(user, message); 1291 return; 1292 } 1293 1294 BMessage info(uint32(job != NULL ? B_OK : B_NAME_NOT_FOUND)); 1295 if (job != NULL) { 1296 _GetBaseJobInfo(job, info); 1297 1298 info.SetInt32("team", job->Team()); 1299 info.SetBool("enabled", job->IsEnabled()); 1300 info.SetBool("running", job->IsRunning()); 1301 info.SetBool("launched", job->IsLaunched()); 1302 info.SetBool("service", job->IsService()); 1303 1304 if (job->Target() != NULL) 1305 info.SetString("target", job->Target()->Name()); 1306 1307 for (int32 i = 0; i < job->Arguments().CountStrings(); i++) 1308 info.AddString("launch", job->Arguments().StringAt(i)); 1309 1310 for (int32 i = 0; i < job->Requirements().CountStrings(); i++) 1311 info.AddString("requires", job->Requirements().StringAt(i)); 1312 1313 PortMap::const_iterator iterator = job->Ports().begin(); 1314 for (; iterator != job->Ports().end(); iterator++) 1315 info.AddMessage("port", &iterator->second); 1316 } 1317 message->SendReply(&info); 1318 } 1319 1320 1321 void 1322 LaunchDaemon::_HandleGetLaunchLog(BMessage* message) 1323 { 1324 uid_t user = _GetUserID(message); 1325 if (user < 0) 1326 return; 1327 1328 BMessage filter; 1329 BString jobName; 1330 const char* event = NULL; 1331 int32 limit = 0; 1332 bool systemOnly = false; 1333 bool userOnly = false; 1334 if (message->FindMessage("filter", &filter) == B_OK) { 1335 limit = filter.GetInt32("limit", 0); 1336 jobName = filter.GetString("job"); 1337 jobName.ToLower(); 1338 event = filter.GetString("event"); 1339 systemOnly = filter.GetBool("systemOnly"); 1340 userOnly = filter.GetBool("userOnly"); 1341 } 1342 1343 BMessage info((uint32)B_OK); 1344 int32 count = 0; 1345 1346 if (user == 0 || !userOnly) { 1347 LogItemList::Iterator iterator = fLog.Iterator(); 1348 while (iterator.HasNext()) { 1349 LogItem* item = iterator.Next(); 1350 if (!item->Matches(jobName.IsEmpty() ? NULL : jobName.String(), 1351 event)) { 1352 continue; 1353 } 1354 1355 BMessage itemMessage; 1356 itemMessage.AddUInt64("when", item->When()); 1357 itemMessage.AddInt32("type", (int32)item->Type()); 1358 itemMessage.AddString("message", item->Message()); 1359 1360 BMessage parameter; 1361 item->GetParameter(parameter); 1362 itemMessage.AddMessage("parameter", ¶meter); 1363 1364 info.AddMessage("item", &itemMessage); 1365 1366 // limit == 0 means no limit 1367 if (++count == limit) 1368 break; 1369 } 1370 } 1371 1372 // Get the list from the user daemon, and merge it into our reply 1373 Session* session = FindSession(user); 1374 if (session != NULL && !systemOnly) { 1375 if (limit != 0) { 1376 // Update limit for user daemon 1377 limit -= count; 1378 if (limit <= 0) { 1379 message->SendReply(&info); 1380 return; 1381 } 1382 } 1383 1384 BMessage reply; 1385 1386 BMessage request(B_GET_LAUNCH_LOG); 1387 status_t status = request.AddInt32("user", 0); 1388 if (status == B_OK && (limit != 0 || !jobName.IsEmpty() 1389 || event != NULL)) { 1390 // Forward filter specification when needed 1391 status = filter.SetInt32("limit", limit); 1392 if (status == B_OK) 1393 status = request.AddMessage("filter", &filter); 1394 } 1395 if (status == B_OK) 1396 status = session->Daemon().SendMessage(&request, &reply); 1397 if (status == B_OK) 1398 info.AddMessage("user", &reply); 1399 } 1400 1401 message->SendReply(&info); 1402 } 1403 1404 1405 uid_t 1406 LaunchDaemon::_GetUserID(BMessage* message) 1407 { 1408 uid_t user = (uid_t)message->GetInt32("user", -1); 1409 if (user < 0) { 1410 BMessage reply((uint32)B_BAD_VALUE); 1411 message->SendReply(&reply); 1412 } 1413 return user; 1414 } 1415 1416 1417 void 1418 LaunchDaemon::_ReadPaths(const BStringList& paths) 1419 { 1420 for (int32 i = 0; i < paths.CountStrings(); i++) { 1421 BEntry entry(paths.StringAt(i)); 1422 if (entry.InitCheck() != B_OK || !entry.Exists()) 1423 continue; 1424 1425 _ReadDirectory(NULL, entry); 1426 } 1427 } 1428 1429 1430 void 1431 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry) 1432 { 1433 if (entry.IsDirectory()) 1434 _ReadDirectory(context, entry); 1435 else 1436 _ReadFile(context, entry); 1437 } 1438 1439 1440 void 1441 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry) 1442 { 1443 BDirectory directory(&directoryEntry); 1444 1445 BEntry entry; 1446 while (directory.GetNextEntry(&entry) == B_OK) { 1447 _ReadEntry(context, entry); 1448 } 1449 } 1450 1451 1452 status_t 1453 LaunchDaemon::_ReadFile(const char* context, BEntry& entry) 1454 { 1455 BPath path; 1456 status_t status = path.SetTo(&entry); 1457 if (status != B_OK) 1458 return status; 1459 1460 SettingsParser parser; 1461 BMessage message; 1462 status = parser.ParseFile(path.Path(), message); 1463 if (status == B_OK) { 1464 TRACE("launch_daemon: read file %s\n", path.Path()); 1465 _AddJobs(NULL, message); 1466 _AddTargets(message); 1467 _AddRunTargets(message); 1468 } 1469 1470 return status; 1471 } 1472 1473 1474 void 1475 LaunchDaemon::_AddJobs(Target* target, BMessage& message) 1476 { 1477 BMessage job; 1478 for (int32 index = 0; message.FindMessage("service", index, 1479 &job) == B_OK; index++) { 1480 _AddJob(target, true, job); 1481 } 1482 1483 for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK; 1484 index++) { 1485 _AddJob(target, false, job); 1486 } 1487 } 1488 1489 1490 void 1491 LaunchDaemon::_AddTargets(BMessage& message) 1492 { 1493 BMessage targetMessage; 1494 for (int32 index = 0; message.FindMessage("target", index, 1495 &targetMessage) == B_OK; index++) { 1496 const char* name = targetMessage.GetString("name"); 1497 if (name == NULL) { 1498 // TODO: log error 1499 debug_printf("Target has no name, ignoring it!\n"); 1500 continue; 1501 } 1502 1503 Target* target = FindTarget(name); 1504 if (target == NULL) { 1505 target = new Target(name); 1506 _AddTarget(target); 1507 } else if (targetMessage.GetBool("reset")) { 1508 // Remove all jobs from this target 1509 for (JobMap::iterator iterator = fJobs.begin(); 1510 iterator != fJobs.end();) { 1511 Job* job = iterator->second; 1512 JobMap::iterator remove = iterator++; 1513 1514 if (job->Target() == target) { 1515 fJobs.erase(remove); 1516 delete job; 1517 } 1518 } 1519 } 1520 1521 _SetCondition(target, targetMessage); 1522 _SetEvent(target, targetMessage); 1523 _SetEnvironment(target, targetMessage); 1524 _AddJobs(target, targetMessage); 1525 1526 if (target->Event() != NULL) 1527 target->Event()->Register(*this); 1528 } 1529 } 1530 1531 1532 void 1533 LaunchDaemon::_AddRunTargets(BMessage& message) 1534 { 1535 BMessage runMessage; 1536 for (int32 index = 0; message.FindMessage("run", index, 1537 &runMessage) == B_OK; index++) { 1538 BMessage conditions; 1539 bool pass = true; 1540 if (runMessage.FindMessage("if", &conditions) == B_OK) { 1541 Condition* condition = Conditions::FromMessage(conditions); 1542 if (condition != NULL) { 1543 pass = condition->Test(*this); 1544 debug_printf("Test: %s -> %d\n", condition->ToString().String(), 1545 pass); 1546 delete condition; 1547 } else 1548 debug_printf("Could not parse condition!\n"); 1549 } 1550 1551 if (pass) { 1552 _AddRunTargets(runMessage, NULL); 1553 _AddRunTargets(runMessage, "then"); 1554 } else { 1555 _AddRunTargets(runMessage, "else"); 1556 } 1557 } 1558 } 1559 1560 1561 void 1562 LaunchDaemon::_AddRunTargets(BMessage& message, const char* name) 1563 { 1564 BMessage targets; 1565 if (name != NULL && message.FindMessage(name, &targets) != B_OK) 1566 return; 1567 1568 const char* target; 1569 for (int32 index = 0; targets.FindString("target", index, &target) == B_OK; 1570 index++) { 1571 fRunTargets.Add(target); 1572 } 1573 } 1574 1575 1576 void 1577 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) 1578 { 1579 BString name = message.GetString("name"); 1580 if (name.IsEmpty()) { 1581 // Invalid job description 1582 return; 1583 } 1584 name.ToLower(); 1585 1586 Job* job = FindJob(name); 1587 if (job == NULL) { 1588 TRACE(" add job \"%s\"\n", name.String()); 1589 1590 job = new (std::nothrow) Job(name); 1591 if (job == NULL) 1592 return; 1593 1594 job->SetTeamListener(this); 1595 job->SetService(service); 1596 job->SetCreateDefaultPort(service); 1597 job->SetTarget(target); 1598 } else 1599 TRACE(" amend job \"%s\"\n", name.String()); 1600 1601 if (message.HasBool("disabled")) { 1602 job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled())); 1603 fLog.JobEnabled(job, job->IsEnabled()); 1604 } 1605 1606 if (message.HasBool("legacy")) 1607 job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); 1608 1609 _SetCondition(job, message); 1610 _SetEvent(job, message); 1611 _SetEnvironment(job, message); 1612 1613 BMessage portMessage; 1614 for (int32 index = 0; 1615 message.FindMessage("port", index, &portMessage) == B_OK; index++) { 1616 job->AddPort(portMessage); 1617 } 1618 1619 if (message.HasString("launch")) 1620 message.FindStrings("launch", &job->Arguments()); 1621 1622 const char* requirement; 1623 for (int32 index = 0; 1624 message.FindString("requires", index, &requirement) == B_OK; 1625 index++) { 1626 job->AddRequirement(requirement); 1627 } 1628 if (fInitTarget != NULL) 1629 job->AddRequirement(fInitTarget->Name()); 1630 1631 fJobs.insert(std::make_pair(job->Title(), job)); 1632 } 1633 1634 1635 /*! Initializes all jobs for the specified target (may be \c NULL). 1636 Jobs that cannot be initialized, and those that never will be due to 1637 conditions, will be removed from the list. 1638 */ 1639 void 1640 LaunchDaemon::_InitJobs(Target* target) 1641 { 1642 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) { 1643 Job* job = iterator->second; 1644 JobMap::iterator remove = iterator++; 1645 1646 if (job->Target() != target) 1647 continue; 1648 1649 status_t status = B_NO_INIT; 1650 if (job->IsEnabled()) { 1651 // Filter out jobs that have a constant and failing condition 1652 if (job->Condition() == NULL || !job->Condition()->IsConstant(*this) 1653 || job->Condition()->Test(*this)) { 1654 std::set<BString> dependencies; 1655 status = job->Init(*this, dependencies); 1656 if (status == B_OK && job->Event() != NULL) 1657 status = job->Event()->Register(*this); 1658 } 1659 } 1660 1661 if (status == B_OK) { 1662 fLog.JobInitialized(job); 1663 } else { 1664 if (status != B_NO_INIT) { 1665 // TODO: log error 1666 debug_printf("Init \"%s\" failed: %s\n", job->Name(), 1667 strerror(status)); 1668 } 1669 fLog.JobIgnored(job, status); 1670 1671 // Remove jobs that won't be used later on 1672 fJobs.erase(remove); 1673 delete job; 1674 } 1675 } 1676 } 1677 1678 1679 /*! Adds all jobs for the specified target (may be \c NULL) to the launch 1680 queue, except those that are triggered by events that haven't been 1681 triggered yet. 1682 1683 Unless \a forceNow is true, the target is only launched if its events, 1684 if any, have been triggered already, and its conditions are met. 1685 */ 1686 void 1687 LaunchDaemon::_LaunchJobs(Target* target, bool forceNow) 1688 { 1689 if (!forceNow && target != NULL && (!target->EventHasTriggered() 1690 || !target->CheckCondition(*this))) { 1691 return; 1692 } 1693 1694 if (target != NULL && !target->HasLaunched()) { 1695 target->SetLaunched(true); 1696 _InitJobs(target); 1697 } 1698 1699 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1700 iterator++) { 1701 Job* job = iterator->second; 1702 if (job->Target() == target) 1703 _LaunchJob(job); 1704 } 1705 } 1706 1707 1708 /*! Stops all running jobs of the specified target (may be \c NULL). 1709 */ 1710 void 1711 LaunchDaemon::_StopJobs(Target* target, bool force) 1712 { 1713 if (target != NULL && !target->HasLaunched()) 1714 return; 1715 1716 for (JobMap::reverse_iterator iterator = fJobs.rbegin(); 1717 iterator != fJobs.rend(); iterator++) { 1718 Job* job = iterator->second; 1719 if (job->Target() == target) 1720 _StopJob(job, force); 1721 } 1722 } 1723 1724 1725 /*! Checks whether or not the specified \a job can be launched. 1726 If \a testOnly is \c false, calling this method will trigger a demand 1727 to the \a job. 1728 */ 1729 bool 1730 LaunchDaemon::_CanLaunchJob(Job* job, uint32 options, bool testOnly) 1731 { 1732 if (job == NULL || !job->CanBeLaunched()) 1733 return false; 1734 1735 return (options & FORCE_NOW) != 0 1736 || (job->EventHasTriggered() && job->CheckCondition(*this) 1737 && ((options & TRIGGER_DEMAND) == 0 1738 || Events::TriggerDemand(job->Event(), testOnly))); 1739 } 1740 1741 1742 /*! Checks recursively if the requirements of the specified job can be launched, 1743 if they are not running already. 1744 Calling this method will not trigger a demand for the requirements. 1745 */ 1746 bool 1747 LaunchDaemon::_CanLaunchJobRequirements(Job* job, uint32 options) 1748 { 1749 int32 count = job->Requirements().CountStrings(); 1750 for (int32 index = 0; index < count; index++) { 1751 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1752 if (requirement != NULL 1753 && !requirement->IsRunning() && !requirement->IsLaunching() 1754 && (!_CanLaunchJob(requirement, options, true) 1755 || _CanLaunchJobRequirements(requirement, options))) { 1756 requirement->AddPending(job->Name()); 1757 return false; 1758 } 1759 } 1760 1761 return true; 1762 } 1763 1764 1765 /*! Adds the specified \a job to the launch queue 1766 queue, except those that are triggered by events. 1767 1768 Unless \c FORCE_NOW is set, the target is only launched if its events, 1769 if any, have been triggered already. 1770 1771 Calling this method will trigger a demand event if \c TRIGGER_DEMAND has 1772 been set. 1773 */ 1774 bool 1775 LaunchDaemon::_LaunchJob(Job* job, uint32 options) 1776 { 1777 if (job != NULL && (job->IsLaunching() || job->IsRunning())) 1778 return true; 1779 1780 if (!_CanLaunchJob(job, options)) 1781 return false; 1782 1783 // Test if we can launch all requirements 1784 if (!_CanLaunchJobRequirements(job, options | TRIGGER_DEMAND)) 1785 return false; 1786 1787 // Actually launch the requirements 1788 int32 count = job->Requirements().CountStrings(); 1789 for (int32 index = 0; index < count; index++) { 1790 Job* requirement = FindJob(job->Requirements().StringAt(index)); 1791 if (requirement != NULL) { 1792 // TODO: For jobs that have their communication channels set up, 1793 // we would not need to trigger demand at this point 1794 if (!_LaunchJob(requirement, options | TRIGGER_DEMAND)) { 1795 // Failed to put a requirement into the launch queue 1796 return false; 1797 } 1798 } 1799 } 1800 1801 if (job->Target() != NULL) 1802 job->Target()->ResolveSourceFiles(); 1803 if (job->Event() != NULL) 1804 job->Event()->ResetTrigger(); 1805 1806 job->SetLaunching(true); 1807 1808 status_t status = fJobQueue.AddJob(job); 1809 if (status != B_OK) { 1810 debug_printf("Adding job %s to queue failed: %s\n", job->Name(), 1811 strerror(status)); 1812 return false; 1813 } 1814 1815 // Try to launch pending jobs as well 1816 count = job->Pending().CountStrings(); 1817 for (int32 index = 0; index < count; index++) { 1818 Job* pending = FindJob(job->Pending().StringAt(index)); 1819 if (pending != NULL && _LaunchJob(pending, 0)) { 1820 // Remove the job from the pending list once its in the launch 1821 // queue, so that is not being launched again next time. 1822 index--; 1823 count--; 1824 } 1825 } 1826 1827 return true; 1828 } 1829 1830 1831 void 1832 LaunchDaemon::_StopJob(Job* job, bool force) 1833 { 1834 // TODO: find out which jobs require this job, and don't stop if any, 1835 // unless force, and then stop them all. 1836 job->SetEnabled(false); 1837 1838 if (!job->IsRunning()) 1839 return; 1840 1841 // Be nice first, and send a simple quit message 1842 BMessenger messenger; 1843 if (job->GetMessenger(messenger) == B_OK) { 1844 BMessage request(B_QUIT_REQUESTED); 1845 messenger.SendMessage(&request); 1846 1847 // TODO: wait a bit before going further 1848 return; 1849 } 1850 // TODO: allow custom shutdown 1851 1852 send_signal(-job->Team(), SIGINT); 1853 // TODO: this would be the next step, again, after a delay 1854 //send_signal(job->Team(), SIGKILL); 1855 } 1856 1857 1858 void 1859 LaunchDaemon::_AddTarget(Target* target) 1860 { 1861 fTargets.insert(std::make_pair(target->Title(), target)); 1862 } 1863 1864 1865 void 1866 LaunchDaemon::_SetCondition(BaseJob* job, const BMessage& message) 1867 { 1868 Condition* condition = job->Condition(); 1869 bool updated = false; 1870 1871 BMessage conditions; 1872 if (message.FindMessage("if", &conditions) == B_OK) { 1873 condition = Conditions::FromMessage(conditions); 1874 updated = true; 1875 } 1876 1877 if (message.GetBool("no_safemode")) { 1878 condition = Conditions::AddNotSafeMode(condition); 1879 updated = true; 1880 } 1881 1882 if (updated) 1883 job->SetCondition(condition); 1884 } 1885 1886 1887 void 1888 LaunchDaemon::_SetEvent(BaseJob* job, const BMessage& message) 1889 { 1890 Event* event = job->Event(); 1891 bool updated = false; 1892 1893 BMessage events; 1894 if (message.FindMessage("on", &events) == B_OK) { 1895 event = Events::FromMessage(this, events); 1896 updated = true; 1897 } 1898 1899 if (message.GetBool("on_demand")) { 1900 event = Events::AddOnDemand(this, event); 1901 updated = true; 1902 } 1903 1904 if (updated) { 1905 TRACE(" event: %s\n", event->ToString().String()); 1906 job->SetEvent(event); 1907 } 1908 } 1909 1910 1911 void 1912 LaunchDaemon::_SetEnvironment(BaseJob* job, const BMessage& message) 1913 { 1914 BMessage environmentMessage; 1915 if (message.FindMessage("env", &environmentMessage) == B_OK) 1916 job->SetEnvironment(environmentMessage); 1917 } 1918 1919 1920 ExternalEventSource* 1921 LaunchDaemon::_FindExternalEventSource(const char* owner, const char* name) const 1922 { 1923 if (name == NULL) 1924 return NULL; 1925 1926 BString eventName = name; 1927 eventName.ToLower(); 1928 1929 EventMap::const_iterator found = fEvents.find(eventName); 1930 if (found != fEvents.end()) 1931 return found->second; 1932 1933 if (owner == NULL) 1934 return NULL; 1935 1936 eventName.Prepend("/"); 1937 eventName.Prepend(get_leaf(owner)); 1938 1939 found = fEvents.find(eventName); 1940 if (found != fEvents.end()) 1941 return found->second; 1942 1943 return NULL; 1944 } 1945 1946 1947 void 1948 LaunchDaemon::_ResolveExternalEvents(ExternalEventSource* eventSource, 1949 const BString& name) 1950 { 1951 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 1952 iterator++) { 1953 Event* externalEvent = Events::ResolveExternalEvent(iterator->second->Event(), 1954 name, eventSource->Flags()); 1955 if (externalEvent != NULL) 1956 eventSource->AddDestination(externalEvent); 1957 } 1958 } 1959 1960 1961 void 1962 LaunchDaemon::_GetBaseJobInfo(BaseJob* job, BMessage& info) 1963 { 1964 info.SetString("name", job->Name()); 1965 1966 if (job->Event() != NULL) 1967 info.SetString("event", job->Event()->ToString()); 1968 1969 if (job->Condition() != NULL) 1970 info.SetString("condition", job->Condition()->ToString()); 1971 } 1972 1973 1974 void 1975 LaunchDaemon::_ForwardEventMessage(uid_t user, BMessage* message) 1976 { 1977 if (fUserMode) 1978 return; 1979 1980 // Forward event to user launch_daemon(s) 1981 if (user == 0) { 1982 for (SessionMap::iterator iterator = fSessions.begin(); 1983 iterator != fSessions.end(); iterator++) { 1984 Session* session = iterator->second; 1985 session->Daemon().SendMessage(message); 1986 // ignore reply 1987 } 1988 } else { 1989 Session* session = FindSession(user); 1990 if (session != NULL) 1991 session->Daemon().SendMessage(message); 1992 } 1993 } 1994 1995 1996 status_t 1997 LaunchDaemon::_StartSession(const char* login) 1998 { 1999 char path[B_PATH_NAME_LENGTH]; 2000 status_t status = get_app_path(path); 2001 if (status != B_OK) 2002 return status; 2003 2004 pid_t pid = -1; 2005 const char* argv[] = {path, login, NULL}; 2006 status = posix_spawn(&pid, path, NULL, NULL, (char* const*)argv, environ); 2007 if (status != B_OK) 2008 return status; 2009 2010 return B_OK; 2011 } 2012 2013 2014 void 2015 LaunchDaemon::_RetrieveKernelOptions() 2016 { 2017 char buffer[32]; 2018 size_t size = sizeof(buffer); 2019 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer, 2020 &size); 2021 if (status == B_OK) { 2022 fSafeMode = !strncasecmp(buffer, "true", size) 2023 || !strncasecmp(buffer, "yes", size) 2024 || !strncasecmp(buffer, "on", size) 2025 || !strncasecmp(buffer, "enabled", size); 2026 } else 2027 fSafeMode = false; 2028 } 2029 2030 2031 void 2032 LaunchDaemon::_SetupEnvironment() 2033 { 2034 // Determine safemode kernel option 2035 setenv("SAFEMODE", IsSafeMode() ? "yes" : "no", true); 2036 2037 // Default locale settings 2038 setenv("LC_TYPE", "en_US.UTF-8", true); 2039 } 2040 2041 2042 /*! Basic system initialization that must happen before any jobs are launched. 2043 */ 2044 void 2045 LaunchDaemon::_InitSystem() 2046 { 2047 #ifndef TEST_MODE 2048 _AddInitJob(new InitRealTimeClockJob()); 2049 _AddInitJob(new InitSharedMemoryDirectoryJob()); 2050 _AddInitJob(new InitTemporaryDirectoryJob()); 2051 #endif 2052 2053 fJobQueue.AddJob(fInitTarget); 2054 } 2055 2056 2057 void 2058 LaunchDaemon::_AddInitJob(BJob* job) 2059 { 2060 fInitTarget->AddDependency(job); 2061 fJobQueue.AddJob(job); 2062 } 2063 2064 2065 // #pragma mark - 2066 2067 2068 #ifndef TEST_MODE 2069 2070 2071 static void 2072 open_stdio(int targetFD, int openMode) 2073 { 2074 #ifdef DEBUG 2075 int fd = open("/dev/dprintf", openMode); 2076 #else 2077 int fd = open("/dev/null", openMode); 2078 #endif 2079 if (fd != targetFD) { 2080 dup2(fd, targetFD); 2081 close(fd); 2082 } 2083 } 2084 2085 2086 #endif // TEST_MODE 2087 2088 2089 static int 2090 user_main(const char* login) 2091 { 2092 struct passwd* passwd = getpwnam(login); 2093 if (passwd == NULL) 2094 return B_NAME_NOT_FOUND; 2095 if (strcmp(passwd->pw_name, login) != 0) 2096 return B_NAME_NOT_FOUND; 2097 2098 // Check if there is a user session running already 2099 uid_t user = passwd->pw_uid; 2100 gid_t group = passwd->pw_gid; 2101 2102 if (setsid() < 0) 2103 exit(EXIT_FAILURE); 2104 2105 if (initgroups(login, group) == -1) 2106 exit(EXIT_FAILURE); 2107 if (setgid(group) != 0) 2108 exit(EXIT_FAILURE); 2109 if (setuid(user) != 0) 2110 exit(EXIT_FAILURE); 2111 2112 if (passwd->pw_dir != NULL && passwd->pw_dir[0] != '\0') { 2113 setenv("HOME", passwd->pw_dir, true); 2114 2115 if (chdir(passwd->pw_dir) != 0) { 2116 debug_printf("Could not switch to home dir %s: %s\n", 2117 passwd->pw_dir, strerror(errno)); 2118 } 2119 } 2120 2121 // TODO: take over system jobs, and reserve their names? (by asking parent) 2122 status_t status; 2123 LaunchDaemon* daemon = new LaunchDaemon(true, status); 2124 if (status == B_OK) 2125 daemon->Run(); 2126 2127 delete daemon; 2128 return 0; 2129 } 2130 2131 2132 int 2133 main(int argc, char* argv[]) 2134 { 2135 if (argc == 2 && geteuid() == 0) 2136 return user_main(argv[1]); 2137 2138 if (find_port(B_LAUNCH_DAEMON_PORT_NAME) >= 0) { 2139 fprintf(stderr, "The launch_daemon is already running!\n"); 2140 return EXIT_FAILURE; 2141 } 2142 2143 #ifndef TEST_MODE 2144 // Make stdin/out/err available 2145 open_stdio(STDIN_FILENO, O_RDONLY); 2146 open_stdio(STDOUT_FILENO, O_WRONLY); 2147 dup2(STDOUT_FILENO, STDERR_FILENO); 2148 #endif 2149 2150 status_t status; 2151 LaunchDaemon* daemon = new LaunchDaemon(false, status); 2152 if (status == B_OK) 2153 daemon->Run(); 2154 2155 delete daemon; 2156 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 2157 } 2158