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 "BaseJob.h"
8
9#include <errno.h>
10#include <stdlib.h>
11#include <string.h>
12#include <unistd.h>
13
14#include <Message.h>
15
16#include "Conditions.h"
17#include "Events.h"
18
19
20BaseJob::BaseJob(const char* name)
21	:
22	BJob(name),
23	fCondition(NULL),
24	fEvent(NULL)
25{
26}
27
28
29BaseJob::~BaseJob()
30{
31	delete fCondition;
32}
33
34
35const char*
36BaseJob::Name() const
37{
38	return Title().String();
39}
40
41
42const ::Condition*
43BaseJob::Condition() const
44{
45	return fCondition;
46}
47
48
49::Condition*
50BaseJob::Condition()
51{
52	return fCondition;
53}
54
55
56void
57BaseJob::SetCondition(::Condition* condition)
58{
59	fCondition = condition;
60}
61
62
63bool
64BaseJob::CheckCondition(ConditionContext& context) const
65{
66	if (fCondition != NULL)
67		return fCondition->Test(context);
68
69	return true;
70}
71
72
73const ::Event*
74BaseJob::Event() const
75{
76	return fEvent;
77}
78
79
80::Event*
81BaseJob::Event()
82{
83	return fEvent;
84}
85
86
87void
88BaseJob::SetEvent(::Event* event)
89{
90	fEvent = event;
91	if (event != NULL)
92		event->SetOwner(this);
93}
94
95
96/*!	Determines whether the events of this job has been triggered
97	already or not.
98	Note, if this job does not have any events, this method returns
99	\c true.
100*/
101bool
102BaseJob::EventHasTriggered() const
103{
104	return Event() == NULL || Event()->Triggered();
105}
106
107
108const BStringList&
109BaseJob::Environment() const
110{
111	return fEnvironment;
112}
113
114
115BStringList&
116BaseJob::Environment()
117{
118	return fEnvironment;
119}
120
121
122const BStringList&
123BaseJob::EnvironmentSourceFiles() const
124{
125	return fSourceFiles;
126}
127
128
129BStringList&
130BaseJob::EnvironmentSourceFiles()
131{
132	return fSourceFiles;
133}
134
135
136void
137BaseJob::SetEnvironment(const BMessage& message)
138{
139	char* name;
140	type_code type;
141	int32 count;
142	for (int32 index = 0; message.GetInfo(B_STRING_TYPE, index, &name, &type,
143			&count) == B_OK; index++) {
144		if (strcmp(name, "from_script") == 0) {
145			const char* fromScript;
146			for (int32 scriptIndex = 0; message.FindString(name, scriptIndex,
147					&fromScript) == B_OK; scriptIndex++) {
148				fSourceFiles.Add(fromScript);
149			}
150			continue;
151		}
152
153		BString variable = name;
154		variable << "=";
155
156		const char* argument;
157		for (int32 argumentIndex = 0; message.FindString(name, argumentIndex,
158				&argument) == B_OK; argumentIndex++) {
159			if (argumentIndex > 0)
160				variable << " ";
161			variable += argument;
162		}
163
164		fEnvironment.Add(variable);
165	}
166}
167
168
169void
170BaseJob::GetSourceFilesEnvironment(BStringList& environment)
171{
172	int32 count = fSourceFiles.CountStrings();
173	for (int32 index = 0; index < count; index++) {
174		_GetSourceFileEnvironment(fSourceFiles.StringAt(index), environment);
175	}
176}
177
178
179/*!	Gets the environment by evaluating the source files, and move that
180	environment to the static environment.
181
182	When this method returns, the source files list will be empty.
183*/
184void
185BaseJob::ResolveSourceFiles()
186{
187	if (fSourceFiles.IsEmpty())
188		return;
189
190	GetSourceFilesEnvironment(fEnvironment);
191	fSourceFiles.MakeEmpty();
192}
193
194
195void
196BaseJob::_GetSourceFileEnvironment(const char* script, BStringList& environment)
197{
198	int pipes[2];
199	if (pipe(&pipes[0]) != 0) {
200		// TODO: log error
201		return;
202	}
203
204	pid_t child = fork();
205	if (child < 0) {
206		// TODO: log error
207		debug_printf("could not fork: %s\n", strerror(errno));
208	} else if (child == 0) {
209		// We're the child, redirect stdout
210		close(STDOUT_FILENO);
211		close(STDERR_FILENO);
212		dup2(pipes[1], STDOUT_FILENO);
213		dup2(pipes[1], STDERR_FILENO);
214
215		for (int32 i = 0; i < 2; i++)
216			close(pipes[i]);
217
218		BString command;
219		command.SetToFormat(". \"%s\"; export -p", script);
220		execl("/bin/sh", "/bin/sh", "-c", command.String(), NULL);
221		exit(1);
222	} else {
223		// Retrieve environment from child
224
225		close(pipes[1]);
226
227		BString line;
228		char buffer[4096];
229		while (true) {
230			ssize_t bytesRead = read(pipes[0], buffer, sizeof(buffer) - 1);
231			if (bytesRead <= 0)
232				break;
233
234			// Make sure the buffer is null terminated
235			buffer[bytesRead] = 0;
236
237			const char* chunk = buffer;
238			while (true) {
239				const char* separator = strchr(chunk, '\n');
240				if (separator == NULL) {
241					line.Append(chunk, bytesRead);
242					break;
243				}
244				line.Append(chunk, separator - chunk);
245				chunk = separator + 1;
246
247				_ParseExportVariable(environment, line);
248				line.Truncate(0);
249			}
250		}
251		if (!line.IsEmpty())
252			_ParseExportVariable(environment, line);
253
254		close(pipes[0]);
255	}
256}
257
258
259void
260BaseJob::_ParseExportVariable(BStringList& environment, const BString& line)
261{
262	if (!line.StartsWith("export "))
263		return;
264
265	int separator = line.FindFirst("=\"");
266	if (separator < 0)
267		return;
268
269	BString variable;
270	line.CopyInto(variable, 7, separator - 7);
271
272	BString value;
273	line.CopyInto(value, separator + 2, line.Length() - separator - 3);
274
275	variable << "=" << value;
276	environment.Add(variable);
277}
278