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