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 "Job.h" 37 #include "Target.h" 38 #include "Worker.h" 39 40 41 using namespace ::BPrivate; 42 using namespace BSupportKit; 43 using BSupportKit::BPrivate::JobQueue; 44 45 46 static const char* kLaunchDirectory = "launch"; 47 48 49 const static settings_template kPortTemplate[] = { 50 {B_STRING_TYPE, "name", NULL, true}, 51 {B_INT32_TYPE, "capacity", NULL}, 52 }; 53 54 const static settings_template kJobTemplate[] = { 55 {B_STRING_TYPE, "name", NULL, true}, 56 {B_BOOL_TYPE, "disabled", NULL}, 57 {B_STRING_TYPE, "launch", NULL}, 58 {B_STRING_TYPE, "requires", NULL}, 59 {B_BOOL_TYPE, "legacy", NULL}, 60 {B_MESSAGE_TYPE, "port", kPortTemplate}, 61 {B_BOOL_TYPE, "no_safemode", NULL}, 62 {0, NULL, NULL} 63 }; 64 65 const static settings_template kTargetTemplate[] = { 66 {B_STRING_TYPE, "name", NULL, true}, 67 {B_BOOL_TYPE, "reset", NULL}, 68 {B_MESSAGE_TYPE, "job", kJobTemplate}, 69 {B_MESSAGE_TYPE, "service", kJobTemplate}, 70 {0, NULL, NULL} 71 }; 72 73 const static settings_template kSettingsTemplate[] = { 74 {B_MESSAGE_TYPE, "target", kTargetTemplate}, 75 {B_MESSAGE_TYPE, "job", kJobTemplate}, 76 {B_MESSAGE_TYPE, "service", kJobTemplate}, 77 {0, NULL, NULL} 78 }; 79 80 81 class Session { 82 public: 83 Session(uid_t user, const BMessenger& target); 84 85 uid_t User() const 86 { return fUser; } 87 const BMessenger& Daemon() const 88 { return fDaemon; } 89 90 private: 91 uid_t fUser; 92 BMessenger fDaemon; 93 }; 94 95 96 typedef std::map<BString, Job*> JobMap; 97 typedef std::map<uid_t, Session*> SessionMap; 98 typedef std::map<BString, Target*> TargetMap; 99 100 101 class LaunchDaemon : public BServer, public Finder { 102 public: 103 LaunchDaemon(bool userMode, status_t& error); 104 virtual ~LaunchDaemon(); 105 106 virtual Job* FindJob(const char* name) const; 107 virtual Target* FindTarget(const char* name) const; 108 Session* FindSession(uid_t user) const; 109 110 virtual void ReadyToRun(); 111 virtual void MessageReceived(BMessage* message); 112 113 private: 114 uid_t _GetUserID(BMessage* message); 115 116 void _ReadPaths(const BStringList& paths); 117 void _ReadEntry(const char* context, BEntry& entry); 118 void _ReadDirectory(const char* context, 119 BEntry& directory); 120 status_t _ReadFile(const char* context, BEntry& entry); 121 122 void _AddJobs(Target* target, BMessage& message); 123 void _AddJob(Target* target, bool service, 124 BMessage& message); 125 void _InitJobs(); 126 void _LaunchJobs(Target* target); 127 void _AddLaunchJob(Job* job); 128 void _AddTarget(Target* target); 129 130 status_t _StartSession(const char* login, 131 const char* password); 132 133 void _RetrieveKernelOptions(); 134 void _SetupEnvironment(); 135 void _InitSystem(); 136 void _AddInitJob(BJob* job); 137 138 bool _IsSafeMode() const; 139 140 private: 141 JobMap fJobs; 142 TargetMap fTargets; 143 JobQueue fJobQueue; 144 SessionMap fSessions; 145 MainWorker* fMainWorker; 146 Target* fInitTarget; 147 bool fSafeMode; 148 bool fUserMode; 149 }; 150 151 152 static const char* 153 get_leaf(const char* signature) 154 { 155 const char* separator = strrchr(signature, '/'); 156 if (separator != NULL) 157 return separator + 1; 158 159 return signature; 160 } 161 162 163 // #pragma mark - 164 165 166 Session::Session(uid_t user, const BMessenger& daemon) 167 : 168 fUser(user), 169 fDaemon(daemon) 170 { 171 } 172 173 174 // #pragma mark - 175 176 177 LaunchDaemon::LaunchDaemon(bool userMode, status_t& error) 178 : 179 BServer(kLaunchDaemonSignature, NULL, 180 create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, 181 userMode ? "AppPort" : B_LAUNCH_DAEMON_PORT_NAME), false, &error), 182 fInitTarget(userMode ? NULL : new Target("init")), 183 fUserMode(userMode) 184 { 185 fMainWorker = new MainWorker(fJobQueue); 186 if (fInitTarget != NULL) 187 _AddTarget(fInitTarget); 188 } 189 190 191 LaunchDaemon::~LaunchDaemon() 192 { 193 } 194 195 196 Job* 197 LaunchDaemon::FindJob(const char* name) const 198 { 199 if (name == NULL) 200 return NULL; 201 202 JobMap::const_iterator found = fJobs.find(BString(name).ToLower()); 203 if (found != fJobs.end()) 204 return found->second; 205 206 return NULL; 207 } 208 209 210 Target* 211 LaunchDaemon::FindTarget(const char* name) const 212 { 213 if (name == NULL) 214 return NULL; 215 216 TargetMap::const_iterator found = fTargets.find(BString(name).ToLower()); 217 if (found != fTargets.end()) 218 return found->second; 219 220 return NULL; 221 } 222 223 224 Session* 225 LaunchDaemon::FindSession(uid_t user) const 226 { 227 SessionMap::const_iterator found = fSessions.find(user); 228 if (found != fSessions.end()) 229 return found->second; 230 231 return NULL; 232 } 233 234 235 void 236 LaunchDaemon::ReadyToRun() 237 { 238 _RetrieveKernelOptions(); 239 _SetupEnvironment(); 240 if (fUserMode) { 241 BLaunchRoster roster; 242 BLaunchRoster::Private(roster).RegisterSessionDaemon(this); 243 } else 244 _InitSystem(); 245 246 BStringList paths; 247 BPathFinder::FindPaths(B_FIND_PATH_DATA_DIRECTORY, kLaunchDirectory, 248 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 249 _ReadPaths(paths); 250 251 BPathFinder::FindPaths(B_FIND_PATH_SETTINGS_DIRECTORY, kLaunchDirectory, 252 fUserMode ? B_FIND_PATHS_USER_ONLY : B_FIND_PATHS_SYSTEM_ONLY, paths); 253 _ReadPaths(paths); 254 255 _InitJobs(); 256 _LaunchJobs(NULL); 257 } 258 259 260 void 261 LaunchDaemon::MessageReceived(BMessage* message) 262 { 263 switch (message->what) { 264 case B_GET_LAUNCH_DATA: 265 { 266 uid_t user = _GetUserID(message); 267 if (user < 0) 268 return; 269 270 BMessage reply((uint32)B_OK); 271 Job* job = FindJob(get_leaf(message->GetString("name"))); 272 if (job == NULL) { 273 Session* session = FindSession(user); 274 if (session != NULL) { 275 // Forward request to user launch_daemon 276 if (session->Daemon().SendMessage(message) == B_OK) 277 break; 278 } 279 reply.what = B_NAME_NOT_FOUND; 280 } else { 281 // If the job has not been launched yet, we'll pass on our 282 // team here. The rationale behind this is that this team 283 // will temporarily own the synchronous reply ports. 284 reply.AddInt32("team", job->Team() < 0 285 ? current_team() : job->Team()); 286 287 PortMap::const_iterator iterator = job->Ports().begin(); 288 for (; iterator != job->Ports().end(); iterator++) { 289 BString name; 290 if (iterator->second.HasString("name")) 291 name << iterator->second.GetString("name") << "_"; 292 name << "port"; 293 294 reply.AddInt32(name.String(), 295 iterator->second.GetInt32("port", -1)); 296 } 297 298 _AddLaunchJob(job); 299 } 300 message->SendReply(&reply); 301 break; 302 } 303 304 case B_LAUNCH_TARGET: 305 { 306 uid_t user = _GetUserID(message); 307 if (user < 0) 308 break; 309 310 const char* name = message->GetString("target"); 311 const char* baseName = message->GetString("base target"); 312 313 Target* target = FindTarget(name); 314 if (target == NULL) { 315 Target* baseTarget = FindTarget(baseName); 316 if (baseTarget != NULL) { 317 target = new Target(name); 318 319 // Copy all jobs with the base target into the new target 320 for (JobMap::iterator iterator = fJobs.begin(); 321 iterator != fJobs.end();) { 322 Job* job = iterator->second; 323 iterator++; 324 325 if (job->Target() == baseTarget) { 326 Job* copy = new Job(*job); 327 copy->SetTarget(target); 328 329 fJobs.insert(std::make_pair(copy->Name(), copy)); 330 } 331 } 332 } 333 } 334 if (target == NULL) { 335 Session* session = FindSession(user); 336 if (session != NULL) { 337 // Forward request to user launch_daemon 338 if (session->Daemon().SendMessage(message) == B_OK) 339 break; 340 } 341 342 BMessage reply(B_NAME_NOT_FOUND); 343 message->SendReply(&reply); 344 break; 345 } 346 347 BMessage data; 348 if (message->FindMessage("data", &data) == B_OK) 349 target->AddData(data.GetString("name"), data); 350 351 _LaunchJobs(target); 352 break; 353 } 354 355 case B_LAUNCH_SESSION: 356 { 357 uid_t user = _GetUserID(message); 358 if (user < 0) 359 break; 360 361 status_t status = B_OK; 362 const char* login = message->GetString("login"); 363 const char* password = message->GetString("password"); 364 if (login == NULL || password == NULL) 365 status = B_BAD_VALUE; 366 if (status == B_OK && user != 0) { 367 // Only the root user can start sessions 368 status = B_PERMISSION_DENIED; 369 } 370 if (status == B_OK) 371 status = _StartSession(login, password); 372 373 BMessage reply((uint32)status); 374 message->SendReply(&reply); 375 break; 376 } 377 378 case B_REGISTER_SESSION_DAEMON: 379 { 380 uid_t user = _GetUserID(message); 381 if (user < 0) 382 break; 383 384 status_t status = B_OK; 385 386 BMessenger target; 387 if (message->FindMessenger("target", &target) != B_OK) 388 status = B_BAD_VALUE; 389 390 if (status == B_OK) { 391 Session* session = new (std::nothrow) Session(user, target); 392 if (session != NULL) 393 fSessions.insert(std::pair<uid_t, Session*>(user, session)); 394 else 395 status = B_NO_MEMORY; 396 } 397 398 BMessage reply((uint32)status); 399 message->SendReply(&reply); 400 break; 401 } 402 403 default: 404 BServer::MessageReceived(message); 405 break; 406 } 407 } 408 409 410 uid_t 411 LaunchDaemon::_GetUserID(BMessage* message) 412 { 413 uid_t user = (uid_t)message->GetInt32("user", -1); 414 if (user < 0) { 415 BMessage reply((uint32)B_BAD_VALUE); 416 message->SendReply(&reply); 417 } 418 return user; 419 } 420 421 422 void 423 LaunchDaemon::_ReadPaths(const BStringList& paths) 424 { 425 for (int32 i = 0; i < paths.CountStrings(); i++) { 426 BEntry entry(paths.StringAt(i)); 427 if (entry.InitCheck() != B_OK || !entry.Exists()) 428 continue; 429 430 _ReadDirectory(NULL, entry); 431 } 432 } 433 434 435 void 436 LaunchDaemon::_ReadEntry(const char* context, BEntry& entry) 437 { 438 if (entry.IsDirectory()) 439 _ReadDirectory(context, entry); 440 else 441 _ReadFile(context, entry); 442 } 443 444 445 void 446 LaunchDaemon::_ReadDirectory(const char* context, BEntry& directoryEntry) 447 { 448 BDirectory directory(&directoryEntry); 449 450 BEntry entry; 451 while (directory.GetNextEntry(&entry) == B_OK) { 452 _ReadEntry(context, entry); 453 } 454 } 455 456 457 status_t 458 LaunchDaemon::_ReadFile(const char* context, BEntry& entry) 459 { 460 DriverSettingsMessageAdapter adapter; 461 462 BPath path; 463 status_t status = path.SetTo(&entry); 464 if (status != B_OK) 465 return status; 466 467 BMessage message; 468 status = adapter.ConvertFromDriverSettings(path.Path(), kSettingsTemplate, 469 message); 470 if (status == B_OK) { 471 _AddJobs(NULL, message); 472 473 BMessage targetMessage; 474 for (int32 index = 0; message.FindMessage("target", index, 475 &targetMessage) == B_OK; index++) { 476 const char* name = targetMessage.GetString("name"); 477 if (name == NULL) { 478 // TODO: log error 479 debug_printf("Target has no name, ignoring it!\n"); 480 continue; 481 } 482 483 Target* target = FindTarget(name); 484 if (target == NULL) { 485 target = new Target(name); 486 _AddTarget(target); 487 } else if (targetMessage.GetBool("reset")) { 488 // Remove all jobs from this target 489 for (JobMap::iterator iterator = fJobs.begin(); 490 iterator != fJobs.end();) { 491 Job* job = iterator->second; 492 JobMap::iterator remove = iterator++; 493 494 if (job->Target() == target) { 495 fJobs.erase(remove); 496 delete job; 497 } 498 } 499 } 500 501 _AddJobs(target, targetMessage); 502 } 503 } 504 505 return status; 506 } 507 508 509 void 510 LaunchDaemon::_AddJobs(Target* target, BMessage& message) 511 { 512 BMessage job; 513 for (int32 index = 0; message.FindMessage("service", index, 514 &job) == B_OK; index++) { 515 _AddJob(target, true, job); 516 } 517 518 for (int32 index = 0; message.FindMessage("job", index, &job) == B_OK; 519 index++) { 520 _AddJob(target, false, job); 521 } 522 } 523 524 525 void 526 LaunchDaemon::_AddJob(Target* target, bool service, BMessage& message) 527 { 528 BString name = message.GetString("name"); 529 if (name.IsEmpty()) { 530 // Invalid job description 531 return; 532 } 533 name.ToLower(); 534 535 Job* job = FindJob(name); 536 if (job == NULL) 537 job = new Job(name); 538 539 job->SetEnabled(!message.GetBool("disabled", !job->IsEnabled())); 540 job->SetService(service); 541 job->SetCreateDefaultPort(!message.GetBool("legacy", !service)); 542 job->SetLaunchInSafeMode( 543 !message.GetBool("no_safemode", !job->LaunchInSafeMode())); 544 job->SetTarget(target); 545 546 BMessage portMessage; 547 for (int32 index = 0; 548 message.FindMessage("port", index, &portMessage) == B_OK; index++) { 549 job->AddPort(portMessage); 550 } 551 552 const char* argument; 553 for (int32 index = 0; 554 message.FindString("launch", index, &argument) == B_OK; index++) { 555 job->AddArgument(argument); 556 } 557 558 const char* requirement; 559 for (int32 index = 0; 560 message.FindString("requires", index, &requirement) == B_OK; 561 index++) { 562 job->AddRequirement(requirement); 563 } 564 if (fInitTarget != NULL) 565 job->AddRequirement(fInitTarget->Name()); 566 567 fJobs.insert(std::pair<BString, Job*>(job->Name(), job)); 568 } 569 570 571 void 572 LaunchDaemon::_InitJobs() 573 { 574 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end();) { 575 Job* job = iterator->second; 576 JobMap::iterator remove = iterator++; 577 578 status_t status = B_NO_INIT; 579 if (job->IsEnabled() && (!_IsSafeMode() || job->LaunchInSafeMode())) { 580 std::set<BString> dependencies; 581 status = job->Init(*this, dependencies); 582 } 583 584 if (status != B_OK) { 585 if (status != B_NO_INIT) { 586 // TODO: log error 587 debug_printf("Init \"%s\" failed: %s\n", job->Name(), 588 strerror(status)); 589 } 590 591 // Remove jobs that won't be used later on 592 fJobs.erase(remove); 593 delete job; 594 } 595 } 596 } 597 598 599 void 600 LaunchDaemon::_LaunchJobs(Target* target) 601 { 602 for (JobMap::iterator iterator = fJobs.begin(); iterator != fJobs.end(); 603 iterator++) { 604 Job* job = iterator->second; 605 if (job->Target() == target) 606 _AddLaunchJob(job); 607 } 608 } 609 610 611 void 612 LaunchDaemon::_AddLaunchJob(Job* job) 613 { 614 if (!job->IsLaunched()) 615 fJobQueue.AddJob(job); 616 } 617 618 619 void 620 LaunchDaemon::_AddTarget(Target* target) 621 { 622 fTargets.insert(std::make_pair(target->Title(), target)); 623 } 624 625 626 status_t 627 LaunchDaemon::_StartSession(const char* login, const char* password) 628 { 629 Unlock(); 630 631 // TODO: enable user/group code and password authentication 632 // The launch_daemon currently cannot talk to the registrar, though 633 /* 634 struct passwd* passwd = getpwnam(login); 635 if (passwd == NULL) 636 return B_NAME_NOT_FOUND; 637 if (strcmp(passwd->pw_name, login) != 0) 638 return B_NAME_NOT_FOUND; 639 640 // TODO: check for auto-login, and ignore password then 641 if (!verify_password(passwd, getspnam(login), password)) 642 return B_PERMISSION_DENIED; 643 644 // Check if there is a user session running already 645 uid_t user = passwd->pw_uid; 646 gid_t group = passwd->pw_gid; 647 */ 648 649 if (fork() == 0) { 650 if (setsid() < 0) 651 exit(EXIT_FAILURE); 652 653 /* 654 debug_printf("session leader...\n"); 655 if (initgroups(login, group) == -1) { 656 debug_printf("1.ouch: %s\n", strerror(errno)); 657 exit(EXIT_FAILURE); 658 } 659 //endgrent(); 660 if (setgid(group) != 0) { 661 debug_printf("2.ouch: %s\n", strerror(errno)); 662 exit(EXIT_FAILURE); 663 } 664 if (setuid(user) != 0) { 665 debug_printf("3.ouch: %s\n", strerror(errno)); 666 exit(EXIT_FAILURE); 667 } 668 */ 669 670 // TODO: This leaks the parent application 671 be_app = NULL; 672 673 // TODO: take over system jobs, and reserve their names 674 status_t status; 675 LaunchDaemon* daemon = new LaunchDaemon(true, status); 676 if (status == B_OK) 677 daemon->Run(); 678 679 delete daemon; 680 exit(EXIT_SUCCESS); 681 } 682 Lock(); 683 return B_OK; 684 } 685 686 687 void 688 LaunchDaemon::_RetrieveKernelOptions() 689 { 690 char buffer[32]; 691 size_t size = sizeof(buffer); 692 status_t status = _kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, buffer, 693 &size); 694 if (status == B_OK) { 695 fSafeMode = !strncasecmp(buffer, "true", size) 696 || !strncasecmp(buffer, "yes", size) 697 || !strncasecmp(buffer, "on", size) 698 || !strncasecmp(buffer, "enabled", size); 699 } else 700 fSafeMode = false; 701 } 702 703 704 void 705 LaunchDaemon::_SetupEnvironment() 706 { 707 // Determine safemode kernel option 708 BString safemode = "SAFEMODE="; 709 safemode << _IsSafeMode() ? "yes" : "no"; 710 711 putenv(safemode.String()); 712 } 713 714 715 /*! Basic system initialization that must happen before any jobs are launched. 716 */ 717 void 718 LaunchDaemon::_InitSystem() 719 { 720 _AddInitJob(new InitRealTimeClockJob()); 721 _AddInitJob(new InitSharedMemoryDirectoryJob()); 722 _AddInitJob(new InitTemporaryDirectoryJob()); 723 724 fJobQueue.AddJob(fInitTarget); 725 } 726 727 728 void 729 LaunchDaemon::_AddInitJob(BJob* job) 730 { 731 fInitTarget->AddDependency(job); 732 fJobQueue.AddJob(job); 733 } 734 735 736 bool 737 LaunchDaemon::_IsSafeMode() const 738 { 739 return fSafeMode; 740 } 741 742 743 // #pragma mark - 744 745 746 int 747 main() 748 { 749 status_t status; 750 LaunchDaemon* daemon = new LaunchDaemon(false, status); 751 if (status == B_OK) 752 daemon->Run(); 753 754 delete daemon; 755 return status == B_OK ? EXIT_SUCCESS : EXIT_FAILURE; 756 } 757