xref: /haiku/src/servers/launch/BaseJob.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 
20 BaseJob::BaseJob(const char* name)
21 	:
22 	BJob(name),
23 	fCondition(NULL),
24 	fEvent(NULL)
25 {
26 }
27 
28 
29 BaseJob::~BaseJob()
30 {
31 	delete fCondition;
32 }
33 
34 
35 const char*
36 BaseJob::Name() const
37 {
38 	return Title().String();
39 }
40 
41 
42 const ::Condition*
43 BaseJob::Condition() const
44 {
45 	return fCondition;
46 }
47 
48 
49 ::Condition*
50 BaseJob::Condition()
51 {
52 	return fCondition;
53 }
54 
55 
56 void
57 BaseJob::SetCondition(::Condition* condition)
58 {
59 	fCondition = condition;
60 }
61 
62 
63 bool
64 BaseJob::CheckCondition(ConditionContext& context) const
65 {
66 	if (fCondition != NULL)
67 		return fCondition->Test(context);
68 
69 	return true;
70 }
71 
72 
73 const ::Event*
74 BaseJob::Event() const
75 {
76 	return fEvent;
77 }
78 
79 
80 ::Event*
81 BaseJob::Event()
82 {
83 	return fEvent;
84 }
85 
86 
87 void
88 BaseJob::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 */
101 bool
102 BaseJob::EventHasTriggered() const
103 {
104 	return Event() == NULL || Event()->Triggered();
105 }
106 
107 
108 const BStringList&
109 BaseJob::Environment() const
110 {
111 	return fEnvironment;
112 }
113 
114 
115 BStringList&
116 BaseJob::Environment()
117 {
118 	return fEnvironment;
119 }
120 
121 
122 const BStringList&
123 BaseJob::EnvironmentSourceFiles() const
124 {
125 	return fSourceFiles;
126 }
127 
128 
129 BStringList&
130 BaseJob::EnvironmentSourceFiles()
131 {
132 	return fSourceFiles;
133 }
134 
135 
136 void
137 BaseJob::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 
169 void
170 BaseJob::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 */
184 void
185 BaseJob::ResolveSourceFiles()
186 {
187 	if (fSourceFiles.IsEmpty())
188 		return;
189 
190 	GetSourceFilesEnvironment(fEnvironment);
191 	fSourceFiles.MakeEmpty();
192 }
193 
194 
195 void
196 BaseJob::_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 
259 void
260 BaseJob::_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