xref: /haiku/src/servers/launch/Job.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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& requirement = Requirements().StringAt(index);
258 		if (dependencies.find(requirement) != dependencies.end()) {
259 			// Found a cyclic dependency
260 			// TODO: log error
261 			return fInitStatus = B_ERROR;
262 		}
263 		dependencies.insert(requirement);
264 
265 		Job* dependency = finder.FindJob(requirement);
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(requirement);
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