xref: /haiku/src/servers/launch/Job.cpp (revision 894526b51a3d931c423878fc0eb8da610fa1fb2a)
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