1 /* 2 * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "Job.h" 8 9 #include <stdlib.h> 10 11 #include <Entry.h> 12 #include <Looper.h> 13 #include <Message.h> 14 #include <Roster.h> 15 16 #include <MessagePrivate.h> 17 #include <RosterPrivate.h> 18 #include <user_group.h> 19 20 #include "Target.h" 21 #include "Utility.h" 22 23 24 Job::Job(const char* name) 25 : 26 BaseJob(name), 27 fEnabled(true), 28 fService(false), 29 fCreateDefaultPort(false), 30 fLaunching(false), 31 fInitStatus(B_NO_INIT), 32 fTeam(-1), 33 fDefaultPort(-1), 34 fToken((uint32)B_PREFERRED_TOKEN), 35 fLaunchStatus(B_NO_INIT), 36 fTarget(NULL), 37 fPendingLaunchDataReplies(0, false), 38 fTeamListener(NULL) 39 { 40 mutex_init(&fLaunchStatusLock, "launch status lock"); 41 } 42 43 44 Job::Job(const Job& other) 45 : 46 BaseJob(other.Name()), 47 fEnabled(other.IsEnabled()), 48 fService(other.IsService()), 49 fCreateDefaultPort(other.CreateDefaultPort()), 50 fLaunching(other.IsLaunching()), 51 fInitStatus(B_NO_INIT), 52 fTeam(-1), 53 fDefaultPort(-1), 54 fToken((uint32)B_PREFERRED_TOKEN), 55 fLaunchStatus(B_NO_INIT), 56 fTarget(other.Target()), 57 fPendingLaunchDataReplies(0, false) 58 { 59 mutex_init(&fLaunchStatusLock, "launch status lock"); 60 61 fCondition = other.fCondition; 62 // TODO: copy events 63 //fEvent = other.fEvent; 64 fEnvironment = other.fEnvironment; 65 fSourceFiles = other.fSourceFiles; 66 67 for (int32 i = 0; i < other.Arguments().CountStrings(); i++) 68 AddArgument(other.Arguments().StringAt(i)); 69 70 for (int32 i = 0; i < other.Requirements().CountStrings(); i++) 71 AddRequirement(other.Requirements().StringAt(i)); 72 73 PortMap::const_iterator constIterator = other.Ports().begin(); 74 for (; constIterator != other.Ports().end(); constIterator++) { 75 fPortMap.insert( 76 std::make_pair(constIterator->first, constIterator->second)); 77 } 78 79 PortMap::iterator iterator = fPortMap.begin(); 80 for (; iterator != fPortMap.end(); iterator++) 81 iterator->second.RemoveData("port"); 82 } 83 84 85 Job::~Job() 86 { 87 _DeletePorts(); 88 } 89 90 91 ::TeamListener* 92 Job::TeamListener() const 93 { 94 return fTeamListener; 95 } 96 97 98 void 99 Job::SetTeamListener(::TeamListener* listener) 100 { 101 fTeamListener = listener; 102 } 103 104 105 bool 106 Job::IsEnabled() const 107 { 108 return fEnabled; 109 } 110 111 112 void 113 Job::SetEnabled(bool enable) 114 { 115 fEnabled = enable; 116 } 117 118 119 bool 120 Job::IsService() const 121 { 122 return fService; 123 } 124 125 126 void 127 Job::SetService(bool service) 128 { 129 fService = service; 130 } 131 132 133 bool 134 Job::CreateDefaultPort() const 135 { 136 return fCreateDefaultPort; 137 } 138 139 140 void 141 Job::SetCreateDefaultPort(bool createPort) 142 { 143 fCreateDefaultPort = createPort; 144 } 145 146 147 void 148 Job::AddPort(BMessage& data) 149 { 150 const char* name = data.GetString("name"); 151 fPortMap.insert(std::pair<BString, BMessage>(BString(name), data)); 152 } 153 154 155 const BStringList& 156 Job::Arguments() const 157 { 158 return fArguments; 159 } 160 161 162 BStringList& 163 Job::Arguments() 164 { 165 return fArguments; 166 } 167 168 169 void 170 Job::AddArgument(const char* argument) 171 { 172 fArguments.Add(argument); 173 } 174 175 176 ::Target* 177 Job::Target() const 178 { 179 return fTarget; 180 } 181 182 183 void 184 Job::SetTarget(::Target* target) 185 { 186 fTarget = target; 187 } 188 189 190 const BStringList& 191 Job::Requirements() const 192 { 193 return fRequirements; 194 } 195 196 197 BStringList& 198 Job::Requirements() 199 { 200 return fRequirements; 201 } 202 203 204 void 205 Job::AddRequirement(const char* requirement) 206 { 207 fRequirements.Add(requirement); 208 } 209 210 211 const BStringList& 212 Job::Pending() const 213 { 214 return fPendingJobs; 215 } 216 217 218 BStringList& 219 Job::Pending() 220 { 221 return fPendingJobs; 222 } 223 224 225 void 226 Job::AddPending(const char* pending) 227 { 228 fPendingJobs.Add(pending); 229 } 230 231 232 bool 233 Job::CheckCondition(ConditionContext& context) const 234 { 235 if (Target() != NULL && !Target()->HasLaunched()) 236 return false; 237 238 return BaseJob::CheckCondition(context); 239 } 240 241 242 status_t 243 Job::Init(const Finder& finder, std::set<BString>& dependencies) 244 { 245 // Only initialize the jobs once 246 if (fInitStatus != B_NO_INIT) 247 return fInitStatus; 248 249 fInitStatus = B_OK; 250 251 if (fTarget != NULL) 252 fTarget->AddDependency(this); 253 254 // Check dependencies 255 256 for (int32 index = 0; index < Requirements().CountStrings(); index++) { 257 const BString& requires = Requirements().StringAt(index); 258 if (dependencies.find(requires) != dependencies.end()) { 259 // Found a cyclic dependency 260 // TODO: log error 261 return fInitStatus = B_ERROR; 262 } 263 dependencies.insert(requires); 264 265 Job* dependency = finder.FindJob(requires); 266 if (dependency != NULL) { 267 std::set<BString> subDependencies = dependencies; 268 269 fInitStatus = dependency->Init(finder, subDependencies); 270 if (fInitStatus != B_OK) { 271 // TODO: log error 272 return fInitStatus; 273 } 274 275 fInitStatus = _AddRequirement(dependency); 276 } else { 277 ::Target* target = finder.FindTarget(requires); 278 if (target != NULL) 279 fInitStatus = _AddRequirement(dependency); 280 else { 281 // Could not find dependency 282 fInitStatus = B_NAME_NOT_FOUND; 283 } 284 } 285 if (fInitStatus != B_OK) { 286 // TODO: log error 287 return fInitStatus; 288 } 289 } 290 291 return fInitStatus; 292 } 293 294 295 status_t 296 Job::InitCheck() const 297 { 298 return fInitStatus; 299 } 300 301 302 team_id 303 Job::Team() const 304 { 305 return fTeam; 306 } 307 308 309 const PortMap& 310 Job::Ports() const 311 { 312 return fPortMap; 313 } 314 315 316 port_id 317 Job::Port(const char* name) const 318 { 319 PortMap::const_iterator found = fPortMap.find(name); 320 if (found != fPortMap.end()) 321 return found->second.GetInt32("port", -1); 322 323 return B_NAME_NOT_FOUND; 324 } 325 326 327 port_id 328 Job::DefaultPort() const 329 { 330 return fDefaultPort; 331 } 332 333 334 void 335 Job::SetDefaultPort(port_id port) 336 { 337 fDefaultPort = port; 338 339 PortMap::iterator iterator = fPortMap.begin(); 340 for (; iterator != fPortMap.end(); iterator++) { 341 BString name; 342 if (iterator->second.HasString("name")) 343 continue; 344 345 iterator->second.SetInt32("port", (int32)port); 346 break; 347 } 348 } 349 350 351 status_t 352 Job::Launch() 353 { 354 // Build environment 355 356 std::vector<const char*> environment; 357 for (const char** variable = (const char**)environ; variable[0] != NULL; 358 variable++) { 359 environment.push_back(variable[0]); 360 } 361 362 if (Target() != NULL) 363 _AddStringList(environment, Target()->Environment()); 364 _AddStringList(environment, Environment()); 365 366 // Resolve source files 367 BStringList sourceFilesEnvironment; 368 GetSourceFilesEnvironment(sourceFilesEnvironment); 369 _AddStringList(environment, sourceFilesEnvironment); 370 371 environment.push_back(NULL); 372 373 if (fArguments.IsEmpty()) { 374 // Launch by signature 375 BString signature("application/"); 376 signature << Name(); 377 378 return _Launch(signature.String(), NULL, 0, NULL, &environment[0]); 379 } 380 381 // Build argument vector 382 383 entry_ref ref; 384 status_t status = get_ref_for_path( 385 Utility::TranslatePath(fArguments.StringAt(0).String()), &ref); 386 if (status != B_OK) { 387 _SetLaunchStatus(status); 388 return status; 389 } 390 391 std::vector<BString> strings; 392 std::vector<const char*> args; 393 394 size_t count = fArguments.CountStrings() - 1; 395 if (count > 0) { 396 for (int32 i = 1; i < fArguments.CountStrings(); i++) { 397 strings.push_back(Utility::TranslatePath(fArguments.StringAt(i))); 398 args.push_back(strings.back()); 399 } 400 args.push_back(NULL); 401 } 402 403 // Launch via entry_ref 404 return _Launch(NULL, &ref, count, &args[0], &environment[0]); 405 } 406 407 408 bool 409 Job::IsLaunched() const 410 { 411 return fLaunchStatus != B_NO_INIT; 412 } 413 414 415 bool 416 Job::IsRunning() const 417 { 418 return fTeam >= 0; 419 } 420 421 422 void 423 Job::TeamDeleted() 424 { 425 fTeam = -1; 426 fDefaultPort = -1; 427 428 if (IsService()) 429 SetState(B_JOB_STATE_WAITING_TO_RUN); 430 431 MutexLocker locker(fLaunchStatusLock); 432 fLaunchStatus = B_NO_INIT; 433 } 434 435 436 bool 437 Job::CanBeLaunched() const 438 { 439 // Services cannot be launched while they are running 440 return IsEnabled() && !IsLaunching() && (!IsService() || !IsRunning()); 441 } 442 443 444 bool 445 Job::IsLaunching() const 446 { 447 return fLaunching; 448 } 449 450 451 void 452 Job::SetLaunching(bool launching) 453 { 454 fLaunching = launching; 455 } 456 457 458 status_t 459 Job::HandleGetLaunchData(BMessage* message) 460 { 461 MutexLocker launchLocker(fLaunchStatusLock); 462 if (IsLaunched()) 463 return _SendLaunchDataReply(message); 464 465 if (!IsEnabled()) 466 return B_NOT_ALLOWED; 467 468 return fPendingLaunchDataReplies.AddItem(message) ? B_OK : B_NO_MEMORY; 469 } 470 471 472 status_t 473 Job::GetMessenger(BMessenger& messenger) 474 { 475 if (fDefaultPort < 0) 476 return B_NAME_NOT_FOUND; 477 478 BMessenger::Private(messenger).SetTo(fTeam, fDefaultPort, fToken); 479 return B_OK; 480 } 481 482 483 status_t 484 Job::Run() 485 { 486 status_t status = BJob::Run(); 487 488 // Jobs can be relaunched at any time 489 if (!IsService()) 490 SetState(B_JOB_STATE_WAITING_TO_RUN); 491 492 return status; 493 } 494 495 496 status_t 497 Job::Execute() 498 { 499 status_t status = B_OK; 500 if (!IsRunning() || !IsService()) 501 status = Launch(); 502 else 503 debug_printf("Ignore launching %s\n", Name()); 504 505 fLaunching = false; 506 return status; 507 } 508 509 510 void 511 Job::_DeletePorts() 512 { 513 PortMap::const_iterator iterator = Ports().begin(); 514 for (; iterator != Ports().end(); iterator++) { 515 port_id port = iterator->second.GetInt32("port", -1); 516 if (port >= 0) 517 delete_port(port); 518 } 519 } 520 521 522 status_t 523 Job::_AddRequirement(BJob* dependency) 524 { 525 if (dependency == NULL) 526 return B_OK; 527 528 switch (dependency->State()) { 529 case B_JOB_STATE_WAITING_TO_RUN: 530 case B_JOB_STATE_STARTED: 531 case B_JOB_STATE_IN_PROGRESS: 532 AddDependency(dependency); 533 break; 534 535 case B_JOB_STATE_SUCCEEDED: 536 // Just queue it without any dependencies 537 break; 538 539 case B_JOB_STATE_FAILED: 540 case B_JOB_STATE_ABORTED: 541 // TODO: return appropriate error 542 return B_BAD_VALUE; 543 } 544 545 return B_OK; 546 } 547 548 549 void 550 Job::_AddStringList(std::vector<const char*>& array, const BStringList& list) 551 { 552 int32 count = list.CountStrings(); 553 for (int32 index = 0; index < count; index++) { 554 array.push_back(list.StringAt(index).String()); 555 } 556 } 557 558 559 void 560 Job::_SetLaunchStatus(status_t launchStatus) 561 { 562 MutexLocker launchLocker(fLaunchStatusLock); 563 fLaunchStatus = launchStatus != B_NO_INIT ? launchStatus : B_ERROR; 564 launchLocker.Unlock(); 565 566 _SendPendingLaunchDataReplies(); 567 } 568 569 570 status_t 571 Job::_SendLaunchDataReply(BMessage* message) 572 { 573 BMessage reply(fTeam < 0 ? fTeam : (uint32)B_OK); 574 if (reply.what == B_OK) { 575 reply.AddInt32("team", fTeam); 576 577 PortMap::const_iterator iterator = fPortMap.begin(); 578 for (; iterator != fPortMap.end(); iterator++) { 579 BString name; 580 if (iterator->second.HasString("name")) 581 name << iterator->second.GetString("name") << "_"; 582 name << "port"; 583 584 reply.AddInt32(name.String(), 585 iterator->second.GetInt32("port", -1)); 586 } 587 } 588 589 message->SendReply(&reply); 590 delete message; 591 return B_OK; 592 } 593 594 595 void 596 Job::_SendPendingLaunchDataReplies() 597 { 598 for (int32 i = 0; i < fPendingLaunchDataReplies.CountItems(); i++) 599 _SendLaunchDataReply(fPendingLaunchDataReplies.ItemAt(i)); 600 601 fPendingLaunchDataReplies.MakeEmpty(); 602 } 603 604 605 /*! Creates the ports for a newly launched job. If the registrar already 606 pre-registered the application, \c fDefaultPort will already be set, and 607 honored when filling the ports message. 608 */ 609 status_t 610 Job::_CreateAndTransferPorts() 611 { 612 // TODO: prefix system ports with "system:" 613 614 bool defaultPort = false; 615 616 for (PortMap::iterator iterator = fPortMap.begin(); 617 iterator != fPortMap.end(); iterator++) { 618 BString name(Name()); 619 const char* suffix = iterator->second.GetString("name"); 620 if (suffix != NULL) 621 name << ':' << suffix; 622 else 623 defaultPort = true; 624 625 const int32 capacity = iterator->second.GetInt32("capacity", 626 B_LOOPER_PORT_DEFAULT_CAPACITY); 627 628 port_id port = -1; 629 if (suffix != NULL || fDefaultPort < 0) { 630 port = _CreateAndTransferPort(name.String(), capacity); 631 if (port < 0) 632 return port; 633 634 if (suffix == NULL) 635 fDefaultPort = port; 636 } else if (suffix == NULL) 637 port = fDefaultPort; 638 639 iterator->second.SetInt32("port", port); 640 641 if (name == "x-vnd.haiku-registrar:auth") { 642 // Allow the launch_daemon to access the registrar authentication 643 BPrivate::set_registrar_authentication_port(port); 644 } 645 } 646 647 if (fCreateDefaultPort && !defaultPort) { 648 BMessage data; 649 data.AddInt32("capacity", B_LOOPER_PORT_DEFAULT_CAPACITY); 650 651 port_id port = -1; 652 if (fDefaultPort < 0) { 653 port = _CreateAndTransferPort(Name(), 654 B_LOOPER_PORT_DEFAULT_CAPACITY); 655 if (port < 0) 656 return port; 657 658 fDefaultPort = port; 659 } else 660 port = fDefaultPort; 661 662 data.SetInt32("port", port); 663 AddPort(data); 664 } 665 666 return B_OK; 667 } 668 669 670 port_id 671 Job::_CreateAndTransferPort(const char* name, int32 capacity) 672 { 673 port_id port = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, Name()); 674 if (port < 0) 675 return port; 676 677 status_t status = set_port_owner(port, fTeam); 678 if (status != B_OK) { 679 delete_port(port); 680 return status; 681 } 682 683 return port; 684 } 685 686 687 status_t 688 Job::_Launch(const char* signature, entry_ref* ref, int argCount, 689 const char* const* args, const char** environment) 690 { 691 thread_id mainThread = -1; 692 status_t result = BRoster::Private().Launch(signature, ref, NULL, argCount, 693 args, environment, &fTeam, &mainThread, &fDefaultPort, NULL, true); 694 if (result == B_OK) { 695 result = _CreateAndTransferPorts(); 696 697 if (result == B_OK) { 698 resume_thread(mainThread); 699 700 if (fTeamListener != NULL) 701 fTeamListener->TeamLaunched(this, result); 702 } else 703 kill_thread(mainThread); 704 } 705 706 _SetLaunchStatus(result); 707 return result; 708 } 709