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