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