⛏️ index : haiku.git

/*
 * Copyright 2015, Axel DΓΆrfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "BaseJob.h"

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <Message.h>

#include "Conditions.h"
#include "Events.h"


BaseJob::BaseJob(const char* name)
	:
	BJob(name),
	fCondition(NULL),
	fEvent(NULL)
{
}


BaseJob::~BaseJob()
{
	delete fCondition;
}


const char*
BaseJob::Name() const
{
	return Title().String();
}


const ::Condition*
BaseJob::Condition() const
{
	return fCondition;
}


::Condition*
BaseJob::Condition()
{
	return fCondition;
}


void
BaseJob::SetCondition(::Condition* condition)
{
	fCondition = condition;
}


bool
BaseJob::CheckCondition(ConditionContext& context) const
{
	if (fCondition != NULL)
		return fCondition->Test(context);

	return true;
}


const ::Event*
BaseJob::Event() const
{
	return fEvent;
}


::Event*
BaseJob::Event()
{
	return fEvent;
}


void
BaseJob::SetEvent(::Event* event)
{
	fEvent = event;
	if (event != NULL)
		event->SetOwner(this);
}


/*!	Determines whether the events of this job has been triggered
	already or not.
	Note, if this job does not have any events, this method returns
	\c true.
*/
bool
BaseJob::EventHasTriggered() const
{
	return Event() == NULL || Event()->Triggered();
}


const BStringList&
BaseJob::Environment() const
{
	return fEnvironment;
}


BStringList&
BaseJob::Environment()
{
	return fEnvironment;
}


const BStringList&
BaseJob::EnvironmentSourceFiles() const
{
	return fSourceFiles;
}


BStringList&
BaseJob::EnvironmentSourceFiles()
{
	return fSourceFiles;
}


void
BaseJob::SetEnvironment(const BMessage& message)
{
	char* name;
	type_code type;
	int32 count;
	for (int32 index = 0; message.GetInfo(B_STRING_TYPE, index, &name, &type,
			&count) == B_OK; index++) {
		if (strcmp(name, "from_script") == 0) {
			const char* fromScript;
			for (int32 scriptIndex = 0; message.FindString(name, scriptIndex,
					&fromScript) == B_OK; scriptIndex++) {
				fSourceFiles.Add(fromScript);
			}
			continue;
		}

		BString variable = name;
		variable << "=";

		const char* argument;
		for (int32 argumentIndex = 0; message.FindString(name, argumentIndex,
				&argument) == B_OK; argumentIndex++) {
			if (argumentIndex > 0)
				variable << " ";
			variable += argument;
		}

		fEnvironment.Add(variable);
	}
}


void
BaseJob::GetSourceFilesEnvironment(BStringList& environment)
{
	int32 count = fSourceFiles.CountStrings();
	for (int32 index = 0; index < count; index++) {
		_GetSourceFileEnvironment(fSourceFiles.StringAt(index), environment);
	}
}


/*!	Gets the environment by evaluating the source files, and move that
	environment to the static environment.

	When this method returns, the source files list will be empty.
*/
void
BaseJob::ResolveSourceFiles()
{
	if (fSourceFiles.IsEmpty())
		return;

	GetSourceFilesEnvironment(fEnvironment);
	fSourceFiles.MakeEmpty();
}


void
BaseJob::_GetSourceFileEnvironment(const char* script, BStringList& environment)
{
	int pipes[2];
	if (pipe(&pipes[0]) != 0) {
		// TODO: log error
		return;
	}

	pid_t child = fork();
	if (child < 0) {
		// TODO: log error
		debug_printf("could not fork: %s\n", strerror(errno));
	} else if (child == 0) {
		// We're the child, redirect stdout
		close(STDOUT_FILENO);
		close(STDERR_FILENO);
		dup2(pipes[1], STDOUT_FILENO);
		dup2(pipes[1], STDERR_FILENO);

		for (int32 i = 0; i < 2; i++)
			close(pipes[i]);

		BString command;
		command.SetToFormat(". \"%s\"; export -p", script);
		execl("/bin/sh", "/bin/sh", "-c", command.String(), NULL);
		exit(1);
	} else {
		// Retrieve environment from child

		close(pipes[1]);

		BString line;
		char buffer[4096];
		while (true) {
			ssize_t bytesRead = read(pipes[0], buffer, sizeof(buffer) - 1);
			if (bytesRead <= 0)
				break;

			// Make sure the buffer is null terminated
			buffer[bytesRead] = 0;

			const char* chunk = buffer;
			while (true) {
				const char* separator = strchr(chunk, '\n');
				if (separator == NULL) {
					line.Append(chunk, bytesRead);
					break;
				}
				line.Append(chunk, separator - chunk);
				chunk = separator + 1;

				_ParseExportVariable(environment, line);
				line.Truncate(0);
			}
		}
		if (!line.IsEmpty())
			_ParseExportVariable(environment, line);

		close(pipes[0]);
	}
}


void
BaseJob::_ParseExportVariable(BStringList& environment, const BString& line)
{
	if (!line.StartsWith("export "))
		return;

	int separator = line.FindFirst("=\"");
	if (separator < 0)
		return;

	BString variable;
	line.CopyInto(variable, 7, separator - 7);

	BString value;
	line.CopyInto(value, separator + 2, line.Length() - separator - 3);

	variable << "=" << value;
	environment.Add(variable);
}