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