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