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