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
Job(const char * name)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
Job(const Job & other)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
~Job()85 Job::~Job()
86 {
87 _DeletePorts();
88 }
89
90
91 ::TeamListener*
TeamListener() const92 Job::TeamListener() const
93 {
94 return fTeamListener;
95 }
96
97
98 void
SetTeamListener(::TeamListener * listener)99 Job::SetTeamListener(::TeamListener* listener)
100 {
101 fTeamListener = listener;
102 }
103
104
105 bool
IsEnabled() const106 Job::IsEnabled() const
107 {
108 return fEnabled;
109 }
110
111
112 void
SetEnabled(bool enable)113 Job::SetEnabled(bool enable)
114 {
115 fEnabled = enable;
116 }
117
118
119 bool
IsService() const120 Job::IsService() const
121 {
122 return fService;
123 }
124
125
126 void
SetService(bool service)127 Job::SetService(bool service)
128 {
129 fService = service;
130 }
131
132
133 bool
CreateDefaultPort() const134 Job::CreateDefaultPort() const
135 {
136 return fCreateDefaultPort;
137 }
138
139
140 void
SetCreateDefaultPort(bool createPort)141 Job::SetCreateDefaultPort(bool createPort)
142 {
143 fCreateDefaultPort = createPort;
144 }
145
146
147 void
AddPort(BMessage & data)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&
Arguments() const156 Job::Arguments() const
157 {
158 return fArguments;
159 }
160
161
162 BStringList&
Arguments()163 Job::Arguments()
164 {
165 return fArguments;
166 }
167
168
169 void
AddArgument(const char * argument)170 Job::AddArgument(const char* argument)
171 {
172 fArguments.Add(argument);
173 }
174
175
176 ::Target*
Target() const177 Job::Target() const
178 {
179 return fTarget;
180 }
181
182
183 void
SetTarget(::Target * target)184 Job::SetTarget(::Target* target)
185 {
186 fTarget = target;
187 }
188
189
190 const BStringList&
Requirements() const191 Job::Requirements() const
192 {
193 return fRequirements;
194 }
195
196
197 BStringList&
Requirements()198 Job::Requirements()
199 {
200 return fRequirements;
201 }
202
203
204 void
AddRequirement(const char * requirement)205 Job::AddRequirement(const char* requirement)
206 {
207 fRequirements.Add(requirement);
208 }
209
210
211 const BStringList&
Pending() const212 Job::Pending() const
213 {
214 return fPendingJobs;
215 }
216
217
218 BStringList&
Pending()219 Job::Pending()
220 {
221 return fPendingJobs;
222 }
223
224
225 void
AddPending(const char * pending)226 Job::AddPending(const char* pending)
227 {
228 fPendingJobs.Add(pending);
229 }
230
231
232 bool
CheckCondition(ConditionContext & context) const233 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
Init(const Finder & finder,std::set<BString> & dependencies)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
InitCheck() const296 Job::InitCheck() const
297 {
298 return fInitStatus;
299 }
300
301
302 team_id
Team() const303 Job::Team() const
304 {
305 return fTeam;
306 }
307
308
309 const PortMap&
Ports() const310 Job::Ports() const
311 {
312 return fPortMap;
313 }
314
315
316 port_id
Port(const char * name) const317 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
DefaultPort() const328 Job::DefaultPort() const
329 {
330 return fDefaultPort;
331 }
332
333
334 void
SetDefaultPort(port_id port)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
Launch()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
IsLaunched() const409 Job::IsLaunched() const
410 {
411 return fLaunchStatus != B_NO_INIT;
412 }
413
414
415 bool
IsRunning() const416 Job::IsRunning() const
417 {
418 return fTeam >= 0;
419 }
420
421
422 void
TeamDeleted()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
CanBeLaunched() const437 Job::CanBeLaunched() const
438 {
439 // Services cannot be launched while they are running
440 return IsEnabled() && !IsLaunching() && (!IsService() || !IsRunning());
441 }
442
443
444 bool
IsLaunching() const445 Job::IsLaunching() const
446 {
447 return fLaunching;
448 }
449
450
451 void
SetLaunching(bool launching)452 Job::SetLaunching(bool launching)
453 {
454 fLaunching = launching;
455 }
456
457
458 status_t
HandleGetLaunchData(BMessage * message)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
GetMessenger(BMessenger & messenger)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
Run()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
Execute()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
_DeletePorts()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
_AddRequirement(BJob * dependency)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
_AddStringList(std::vector<const char * > & array,const BStringList & list)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
_SetLaunchStatus(status_t launchStatus)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
_SendLaunchDataReply(BMessage * message)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
_SendPendingLaunchDataReplies()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
_CreateAndTransferPorts()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
_CreateAndTransferPort(const char * name,int32 capacity)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
_Launch(const char * signature,entry_ref * ref,int argCount,const char * const * args,const char ** environment)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