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 fInitStatus(B_NO_INIT), 29 fTeam(-1), 30 fTarget(NULL) 31 { 32 } 33 34 35 Job::Job(const Job& other) 36 : 37 BaseJob(other.Name()), 38 fEnabled(other.IsEnabled()), 39 fService(other.IsService()), 40 fCreateDefaultPort(other.CreateDefaultPort()), 41 fInitStatus(B_NO_INIT), 42 fTeam(-1), 43 fTarget(other.Target()) 44 { 45 fCondition = other.fCondition; 46 // TODO: copy events 47 //fEvent = other.fEvent; 48 fEnvironment = other.fEnvironment; 49 fSourceFiles = other.fSourceFiles; 50 51 for (int32 i = 0; i < other.Arguments().CountStrings(); i++) 52 AddArgument(other.Arguments().StringAt(i)); 53 54 for (int32 i = 0; i < other.Requirements().CountStrings(); i++) 55 AddRequirement(other.Requirements().StringAt(i)); 56 57 PortMap::const_iterator constIterator = other.Ports().begin(); 58 for (; constIterator != other.Ports().end(); constIterator++) { 59 fPortMap.insert( 60 std::make_pair(constIterator->first, constIterator->second)); 61 } 62 63 PortMap::iterator iterator = fPortMap.begin(); 64 for (; iterator != fPortMap.end(); iterator++) 65 iterator->second.RemoveData("port"); 66 } 67 68 69 Job::~Job() 70 { 71 _DeletePorts(); 72 } 73 74 75 bool 76 Job::IsEnabled() const 77 { 78 return fEnabled; 79 } 80 81 82 void 83 Job::SetEnabled(bool enable) 84 { 85 fEnabled = enable; 86 } 87 88 89 bool 90 Job::IsService() const 91 { 92 return fService; 93 } 94 95 96 void 97 Job::SetService(bool service) 98 { 99 fService = service; 100 } 101 102 103 bool 104 Job::CreateDefaultPort() const 105 { 106 return fCreateDefaultPort; 107 } 108 109 110 void 111 Job::SetCreateDefaultPort(bool createPort) 112 { 113 fCreateDefaultPort = createPort; 114 } 115 116 117 void 118 Job::AddPort(BMessage& data) 119 { 120 const char* name = data.GetString("name"); 121 fPortMap.insert(std::pair<BString, BMessage>(BString(name), data)); 122 } 123 124 125 const BStringList& 126 Job::Arguments() const 127 { 128 return fArguments; 129 } 130 131 132 BStringList& 133 Job::Arguments() 134 { 135 return fArguments; 136 } 137 138 139 void 140 Job::AddArgument(const char* argument) 141 { 142 fArguments.Add(argument); 143 } 144 145 146 ::Target* 147 Job::Target() const 148 { 149 return fTarget; 150 } 151 152 153 void 154 Job::SetTarget(::Target* target) 155 { 156 fTarget = target; 157 } 158 159 160 const BStringList& 161 Job::Requirements() const 162 { 163 return fRequirements; 164 } 165 166 167 BStringList& 168 Job::Requirements() 169 { 170 return fRequirements; 171 } 172 173 174 void 175 Job::AddRequirement(const char* requirement) 176 { 177 fRequirements.Add(requirement); 178 } 179 180 181 bool 182 Job::CheckCondition(ConditionContext& context) const 183 { 184 if (Target() != NULL && !Target()->HasLaunched()) 185 return false; 186 187 return BaseJob::CheckCondition(context); 188 } 189 190 191 status_t 192 Job::Init(const Finder& finder, std::set<BString>& dependencies) 193 { 194 // Only initialize the jobs once 195 if (fInitStatus != B_NO_INIT) 196 return fInitStatus; 197 198 fInitStatus = B_OK; 199 200 if (fTarget != NULL) 201 fTarget->AddDependency(this); 202 203 // Check dependencies 204 205 for (int32 index = 0; index < Requirements().CountStrings(); index++) { 206 const BString& requires = Requirements().StringAt(index); 207 if (dependencies.find(requires) != dependencies.end()) { 208 // Found a cyclic dependency 209 // TODO: log error 210 return fInitStatus = B_ERROR; 211 } 212 dependencies.insert(requires); 213 214 Job* dependency = finder.FindJob(requires); 215 if (dependency != NULL) { 216 std::set<BString> subDependencies = dependencies; 217 218 fInitStatus = dependency->Init(finder, subDependencies); 219 if (fInitStatus != B_OK) { 220 // TODO: log error 221 return fInitStatus; 222 } 223 224 fInitStatus = _AddRequirement(dependency); 225 } else { 226 ::Target* target = finder.FindTarget(requires); 227 if (target != NULL) 228 fInitStatus = _AddRequirement(dependency); 229 else { 230 // Could not find dependency 231 fInitStatus = B_NAME_NOT_FOUND; 232 } 233 } 234 if (fInitStatus != B_OK) { 235 // TODO: log error 236 return fInitStatus; 237 } 238 } 239 240 // Create ports 241 // TODO: prefix system ports with "system:" 242 243 bool defaultPort = false; 244 245 for (PortMap::iterator iterator = fPortMap.begin(); 246 iterator != fPortMap.end(); iterator++) { 247 BString name(Name()); 248 const char* suffix = iterator->second.GetString("name"); 249 if (suffix != NULL) 250 name << ':' << suffix; 251 else 252 defaultPort = true; 253 254 const int32 capacity = iterator->second.GetInt32("capacity", 255 B_LOOPER_PORT_DEFAULT_CAPACITY); 256 257 port_id port = create_port(capacity, name.String()); 258 if (port < 0) { 259 fInitStatus = port; 260 break; 261 } 262 iterator->second.SetInt32("port", port); 263 264 if (name == "x-vnd.haiku-registrar:auth") { 265 // Allow the launch_daemon to access the registrar authentication 266 BPrivate::set_registrar_authentication_port(port); 267 } 268 } 269 270 if (fInitStatus == B_OK && fCreateDefaultPort && !defaultPort) { 271 BMessage data; 272 data.AddInt32("capacity", B_LOOPER_PORT_DEFAULT_CAPACITY); 273 274 port_id port = create_port(B_LOOPER_PORT_DEFAULT_CAPACITY, Name()); 275 if (port < 0) { 276 // TODO: log error 277 fInitStatus = port; 278 } else { 279 data.SetInt32("port", port); 280 AddPort(data); 281 } 282 } 283 284 return fInitStatus; 285 } 286 287 288 status_t 289 Job::InitCheck() const 290 { 291 return fInitStatus; 292 } 293 294 295 team_id 296 Job::Team() const 297 { 298 return fTeam; 299 } 300 301 302 const PortMap& 303 Job::Ports() const 304 { 305 return fPortMap; 306 } 307 308 309 port_id 310 Job::Port(const char* name) const 311 { 312 PortMap::const_iterator found = fPortMap.find(name); 313 if (found != fPortMap.end()) 314 return found->second.GetInt32("port", -1); 315 316 return B_NAME_NOT_FOUND; 317 } 318 319 320 status_t 321 Job::Launch() 322 { 323 // Build environment 324 325 std::vector<const char*> environment; 326 for (const char** variable = (const char**)environ; variable[0] != NULL; 327 variable++) { 328 environment.push_back(variable[0]); 329 } 330 331 if (Target() != NULL) 332 _AddStringList(environment, Target()->Environment()); 333 _AddStringList(environment, Environment()); 334 335 // Resolve source files 336 BStringList sourceFilesEnvironment; 337 GetSourceFilesEnvironment(sourceFilesEnvironment); 338 _AddStringList(environment, sourceFilesEnvironment); 339 340 environment.push_back(NULL); 341 342 if (fArguments.IsEmpty()) { 343 // Launch by signature 344 BString signature("application/"); 345 signature << Name(); 346 return BRoster::Private().Launch(signature.String(), NULL, NULL, 347 0, NULL, &environment[0], &fTeam); 348 } 349 350 // Build argument vector 351 352 entry_ref ref; 353 status_t status = get_ref_for_path(fArguments.StringAt(0).String(), &ref); 354 if (status != B_OK) 355 return status; 356 357 std::vector<const char*> args; 358 359 size_t count = fArguments.CountStrings() - 1; 360 if (count > 0) { 361 for (int32 i = 1; i < fArguments.CountStrings(); i++) { 362 args.push_back(fArguments.StringAt(i)); 363 } 364 args.push_back(NULL); 365 } 366 367 // Launch via entry_ref 368 return BRoster::Private().Launch(NULL, &ref, NULL, count, &args[0], 369 &environment[0], &fTeam); 370 } 371 372 373 bool 374 Job::IsLaunched() const 375 { 376 return fTeam >= 0; 377 } 378 379 380 status_t 381 Job::Execute() 382 { 383 if (!IsLaunched()) 384 return Launch(); 385 386 return B_OK; 387 } 388 389 390 void 391 Job::_DeletePorts() 392 { 393 PortMap::const_iterator iterator = Ports().begin(); 394 for (; iterator != Ports().end(); iterator++) { 395 port_id port = iterator->second.GetInt32("port", -1); 396 if (port >= 0) 397 delete_port(port); 398 } 399 } 400 401 402 status_t 403 Job::_AddRequirement(BJob* dependency) 404 { 405 if (dependency == NULL) 406 return B_OK; 407 408 switch (dependency->State()) { 409 case B_JOB_STATE_WAITING_TO_RUN: 410 case B_JOB_STATE_STARTED: 411 case B_JOB_STATE_IN_PROGRESS: 412 AddDependency(dependency); 413 break; 414 415 case B_JOB_STATE_SUCCEEDED: 416 // Just queue it without any dependencies 417 break; 418 419 case B_JOB_STATE_FAILED: 420 case B_JOB_STATE_ABORTED: 421 // TODO: return appropriate error 422 return B_BAD_VALUE; 423 } 424 425 return B_OK; 426 } 427 428 429 void 430 Job::_AddStringList(std::vector<const char*>& array, const BStringList& list) 431 { 432 int32 count = list.CountStrings(); 433 for (int32 index = 0; index < count; index++) { 434 array.push_back(list.StringAt(index).String()); 435 } 436 } 437