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