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 "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 <RosterPrivate.h> 17 #include <user_group.h> 18 19 #include "Target.h" 20 21 22 Job::Job(const char* name) 23 : 24 BaseJob(name), 25 fEnabled(true), 26 fService(false), 27 fCreateDefaultPort(false), 28 fLaunching(false), 29 fInitStatus(B_NO_INIT), 30 fTeam(-1), 31 fLaunchStatus(B_NO_INIT), 32 fTarget(NULL), 33 fPendingLaunchDataReplies(0, false) 34 { 35 mutex_init(&fLaunchStatusLock, "launch status lock"); 36 } 37 38 39 Job::Job(const Job& other) 40 : 41 BaseJob(other.Name()), 42 fEnabled(other.IsEnabled()), 43 fService(other.IsService()), 44 fCreateDefaultPort(other.CreateDefaultPort()), 45 fInitStatus(B_NO_INIT), 46 fTeam(-1), 47 fLaunchStatus(B_NO_INIT), 48 fTarget(other.Target()), 49 fPendingLaunchDataReplies(0, false) 50 { 51 mutex_init(&fLaunchStatusLock, "launch status lock"); 52 53 fCondition = other.fCondition; 54 // TODO: copy events 55 //fEvent = other.fEvent; 56 fEnvironment = other.fEnvironment; 57 fSourceFiles = other.fSourceFiles; 58 59 for (int32 i = 0; i < other.Arguments().CountStrings(); i++) 60 AddArgument(other.Arguments().StringAt(i)); 61 62 for (int32 i = 0; i < other.Requirements().CountStrings(); i++) 63 AddRequirement(other.Requirements().StringAt(i)); 64 65 PortMap::const_iterator constIterator = other.Ports().begin(); 66 for (; constIterator != other.Ports().end(); constIterator++) { 67 fPortMap.insert( 68 std::make_pair(constIterator->first, constIterator->second)); 69 } 70 71 PortMap::iterator iterator = fPortMap.begin(); 72 for (; iterator != fPortMap.end(); iterator++) 73 iterator->second.RemoveData("port"); 74 } 75 76 77 Job::~Job() 78 { 79 _DeletePorts(); 80 } 81 82 83 bool 84 Job::IsEnabled() const 85 { 86 return fEnabled; 87 } 88 89 90 void 91 Job::SetEnabled(bool enable) 92 { 93 fEnabled = enable; 94 } 95 96 97 bool 98 Job::IsService() const 99 { 100 return fService; 101 } 102 103 104 void 105 Job::SetService(bool service) 106 { 107 fService = service; 108 } 109 110 111 bool 112 Job::CreateDefaultPort() const 113 { 114 return fCreateDefaultPort; 115 } 116 117 118 void 119 Job::SetCreateDefaultPort(bool createPort) 120 { 121 fCreateDefaultPort = createPort; 122 } 123 124 125 void 126 Job::AddPort(BMessage& data) 127 { 128 const char* name = data.GetString("name"); 129 fPortMap.insert(std::pair<BString, BMessage>(BString(name), data)); 130 } 131 132 133 const BStringList& 134 Job::Arguments() const 135 { 136 return fArguments; 137 } 138 139 140 BStringList& 141 Job::Arguments() 142 { 143 return fArguments; 144 } 145 146 147 void 148 Job::AddArgument(const char* argument) 149 { 150 fArguments.Add(argument); 151 } 152 153 154 ::Target* 155 Job::Target() const 156 { 157 return fTarget; 158 } 159 160 161 void 162 Job::SetTarget(::Target* target) 163 { 164 fTarget = target; 165 } 166 167 168 const BStringList& 169 Job::Requirements() const 170 { 171 return fRequirements; 172 } 173 174 175 BStringList& 176 Job::Requirements() 177 { 178 return fRequirements; 179 } 180 181 182 void 183 Job::AddRequirement(const char* requirement) 184 { 185 fRequirements.Add(requirement); 186 } 187 188 189 bool 190 Job::CheckCondition(ConditionContext& context) const 191 { 192 if (Target() != NULL && !Target()->HasLaunched()) 193 return false; 194 195 return BaseJob::CheckCondition(context); 196 } 197 198 199 status_t 200 Job::Init(const Finder& finder, std::set<BString>& dependencies) 201 { 202 // Only initialize the jobs once 203 if (fInitStatus != B_NO_INIT) 204 return fInitStatus; 205 206 fInitStatus = B_OK; 207 208 if (fTarget != NULL) 209 fTarget->AddDependency(this); 210 211 // Check dependencies 212 213 for (int32 index = 0; index < Requirements().CountStrings(); index++) { 214 const BString& requires = Requirements().StringAt(index); 215 if (dependencies.find(requires) != dependencies.end()) { 216 // Found a cyclic dependency 217 // TODO: log error 218 return fInitStatus = B_ERROR; 219 } 220 dependencies.insert(requires); 221 222 Job* dependency = finder.FindJob(requires); 223 if (dependency != NULL) { 224 std::set<BString> subDependencies = dependencies; 225 226 fInitStatus = dependency->Init(finder, subDependencies); 227 if (fInitStatus != B_OK) { 228 // TODO: log error 229 return fInitStatus; 230 } 231 232 fInitStatus = _AddRequirement(dependency); 233 } else { 234 ::Target* target = finder.FindTarget(requires); 235 if (target != NULL) 236 fInitStatus = _AddRequirement(dependency); 237 else { 238 // Could not find dependency 239 fInitStatus = B_NAME_NOT_FOUND; 240 } 241 } 242 if (fInitStatus != B_OK) { 243 // TODO: log error 244 return fInitStatus; 245 } 246 } 247 248 return fInitStatus; 249 } 250 251 252 status_t 253 Job::InitCheck() const 254 { 255 return fInitStatus; 256 } 257 258 259 team_id 260 Job::Team() const 261 { 262 return fTeam; 263 } 264 265 266 const PortMap& 267 Job::Ports() const 268 { 269 return fPortMap; 270 } 271 272 273 port_id 274 Job::Port(const char* name) const 275 { 276 PortMap::const_iterator found = fPortMap.find(name); 277 if (found != fPortMap.end()) 278 return found->second.GetInt32("port", -1); 279 280 return B_NAME_NOT_FOUND; 281 } 282 283 284 status_t 285 Job::Launch() 286 { 287 // Build environment 288 289 std::vector<const char*> environment; 290 for (const char** variable = (const char**)environ; variable[0] != NULL; 291 variable++) { 292 environment.push_back(variable[0]); 293 } 294 295 if (Target() != NULL) 296 _AddStringList(environment, Target()->Environment()); 297 _AddStringList(environment, Environment()); 298 299 // Resolve source files 300 BStringList sourceFilesEnvironment; 301 GetSourceFilesEnvironment(sourceFilesEnvironment); 302 _AddStringList(environment, sourceFilesEnvironment); 303 304 environment.push_back(NULL); 305 306 if (fArguments.IsEmpty()) { 307 // Launch by signature 308 BString signature("application/"); 309 signature << Name(); 310 311 return _Launch(signature.String(), NULL, 0, NULL, &environment[0]); 312 } 313 314 // Build argument vector 315 316 entry_ref ref; 317 status_t status = get_ref_for_path(fArguments.StringAt(0).String(), &ref); 318 if (status != B_OK) { 319 _SetLaunchStatus(status); 320 return status; 321 } 322 323 std::vector<const char*> args; 324 325 size_t count = fArguments.CountStrings() - 1; 326 if (count > 0) { 327 for (int32 i = 1; i < fArguments.CountStrings(); i++) { 328 args.push_back(fArguments.StringAt(i)); 329 } 330 args.push_back(NULL); 331 } 332 333 // Launch via entry_ref 334 return _Launch(NULL, &ref, count, &args[0], &environment[0]); 335 } 336 337 338 bool 339 Job::IsLaunched() const 340 { 341 return fLaunchStatus != B_NO_INIT; 342 } 343 344 345 bool 346 Job::IsRunning() const 347 { 348 // TODO: monitor team status; should jobs be allowed to run multiple times? 349 return State() == B_JOB_STATE_SUCCEEDED && IsLaunched() && IsService(); 350 } 351 352 353 bool 354 Job::IsLaunching() const 355 { 356 return fLaunching; 357 } 358 359 360 void 361 Job::SetLaunching(bool launching) 362 { 363 fLaunching = launching; 364 } 365 366 367 status_t 368 Job::HandleGetLaunchData(BMessage* message) 369 { 370 MutexLocker launchLocker(fLaunchStatusLock); 371 if (IsLaunched()) 372 return _SendLaunchDataReply(message); 373 374 return fPendingLaunchDataReplies.AddItem(message) ? B_OK : B_NO_MEMORY; 375 } 376 377 378 status_t 379 Job::Run() 380 { 381 status_t status = BJob::Run(); 382 383 // TODO: monitor team, don't just do this 384 if (!IsService()) 385 SetState(B_JOB_STATE_WAITING_TO_RUN); 386 387 return status; 388 } 389 390 391 status_t 392 Job::Execute() 393 { 394 status_t status = B_OK; 395 if (!IsLaunched() || !IsService()) 396 status = Launch(); 397 398 fLaunching = false; 399 return status; 400 } 401 402 403 void 404 Job::_DeletePorts() 405 { 406 PortMap::const_iterator iterator = Ports().begin(); 407 for (; iterator != Ports().end(); iterator++) { 408 port_id port = iterator->second.GetInt32("port", -1); 409 if (port >= 0) 410 delete_port(port); 411 } 412 } 413 414 415 status_t 416 Job::_AddRequirement(BJob* dependency) 417 { 418 if (dependency == NULL) 419 return B_OK; 420 421 switch (dependency->State()) { 422 case B_JOB_STATE_WAITING_TO_RUN: 423 case B_JOB_STATE_STARTED: 424 case B_JOB_STATE_IN_PROGRESS: 425 AddDependency(dependency); 426 break; 427 428 case B_JOB_STATE_SUCCEEDED: 429 // Just queue it without any dependencies 430 break; 431 432 case B_JOB_STATE_FAILED: 433 case B_JOB_STATE_ABORTED: 434 // TODO: return appropriate error 435 return B_BAD_VALUE; 436 } 437 438 return B_OK; 439 } 440 441 442 void 443 Job::_AddStringList(std::vector<const char*>& array, const BStringList& list) 444 { 445 int32 count = list.CountStrings(); 446 for (int32 index = 0; index < count; index++) { 447 array.push_back(list.StringAt(index).String()); 448 } 449 } 450 451 452 void 453 Job::_SetLaunchStatus(status_t launchStatus) 454 { 455 MutexLocker launchLocker(fLaunchStatusLock); 456 fLaunchStatus = launchStatus != B_NO_INIT ? launchStatus : B_ERROR; 457 launchLocker.Unlock(); 458 459 _SendPendingLaunchDataReplies(); 460 } 461 462 463 status_t 464 Job::_SendLaunchDataReply(BMessage* message) 465 { 466 BMessage reply(fTeam < 0 ? fTeam : (uint32)B_OK); 467 if (reply.what == B_OK) { 468 reply.AddInt32("team", fTeam); 469 470 PortMap::const_iterator iterator = fPortMap.begin(); 471 for (; iterator != fPortMap.end(); iterator++) { 472 BString name; 473 if (iterator->second.HasString("name")) 474 name << iterator->second.GetString("name") << "_"; 475 name << "port"; 476 477 reply.AddInt32(name.String(), 478 iterator->second.GetInt32("port", -1)); 479 } 480 } 481 482 message->SendReply(&reply); 483 delete message; 484 return B_OK; 485 } 486 487 488 void 489 Job::_SendPendingLaunchDataReplies() 490 { 491 for (int32 i = 0; i < fPendingLaunchDataReplies.CountItems(); i++) 492 _SendLaunchDataReply(fPendingLaunchDataReplies.ItemAt(i)); 493 494 fPendingLaunchDataReplies.MakeEmpty(); 495 } 496 497 498 status_t 499 Job::_CreateAndTransferPorts() 500 { 501 // TODO: prefix system ports with "system:" 502 503 bool defaultPort = false; 504 505 for (PortMap::iterator iterator = fPortMap.begin(); 506 iterator != fPortMap.end(); iterator++) { 507 BString name(Name()); 508 const char* suffix = iterator->second.GetString("name"); 509 if (suffix != NULL) 510 name << ':' << suffix; 511 else 512 defaultPort = true; 513 514 const int32 capacity = iterator->second.GetInt32("capacity", 515 B_LOOPER_PORT_DEFAULT_CAPACITY); 516 517 port_id port = create_port(capacity, name.String()); 518 if (port < 0) 519 return port; 520 521 status_t result = set_port_owner(port, fTeam); 522 if (result != B_OK) 523 return result; 524 525 iterator->second.SetInt32("port", port); 526 527 if (name == "x-vnd.haiku-registrar:auth") { 528 // Allow the launch_daemon to access the registrar authentication 529 BPrivate::set_registrar_authentication_port(port); 530 } 531 } 532 533 if (fCreateDefaultPort && !defaultPort) { 534 BMessage data; 535 data.AddInt32("capacity", B_LOOPER_PORT_DEFAULT_CAPACITY); 536 537 port_id port = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, Name()); 538 if (port < 0) 539 return port; 540 541 status_t result = set_port_owner(port, fTeam); 542 if (result != B_OK) 543 return result; 544 545 data.SetInt32("port", port); 546 AddPort(data); 547 } 548 549 return B_OK; 550 } 551 552 553 status_t 554 Job::_Launch(const char* signature, entry_ref* ref, int argCount, 555 const char* const* args, const char** environment) 556 { 557 thread_id mainThread = -1; 558 status_t result = BRoster::Private().Launch(signature, ref, NULL, argCount, 559 args, environment, &fTeam, &mainThread, true); 560 561 if (result == B_OK) { 562 result = _CreateAndTransferPorts(); 563 564 if (result == B_OK) 565 resume_thread(mainThread); 566 else 567 kill_thread(mainThread); 568 } 569 570 _SetLaunchStatus(result); 571 return result; 572 } 573