Merge branch 'LinuxProcess' of git://github.com/nothingTVatYT/FlaxEngine into nothingTVatYT-LinuxProcess

This commit is contained in:
Wojtek Figat
2021-10-21 19:04:30 +02:00

View File

@@ -34,6 +34,7 @@
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <pthread.h>
#include <unistd.h>
#include <sched.h>
@@ -2770,80 +2771,102 @@ bool LinuxPlatform::SetEnvironmentVariable(const String& name, const String& val
return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0;
}
int32 LinuxProcess(const StringView& cmdLine, const StringView& workingDir, const Dictionary<String, String>& environment, bool waitForEnd, bool logOutput) {
int fildes[2];
int32 returnCode = 0;
//String cmdLine(commandLine);
if (logOutput && pipe(fildes) < 0) {
LOG(Warning, "cannot create a pipe, errno={}", errno);
}
pid_t pid = fork();
if (pid < 0) {
LOG(Warning, "cannot fork, errno={}", errno);
return errno;
} else if (pid == 0) {
// child process
int ret;
const char* const cmd[] = { "sh", "-c", StringAnsi(cmdLine).GetText(), (char *)0 };
// we could use the execve and supply a list of variable assignments but as we would have to build
// and quote the values there is hardly any benefit over using setenv() calls
if (!environment.IsEmpty()) {
for (auto& e : environment) {
setenv(StringAnsi(e.Key).GetText(), StringAnsi(e.Value).GetText(), 1);
}
}
if (workingDir.HasChars()) {
if (chdir(StringAnsi(workingDir).GetText()) != 0) {
LOG(Warning, "cannot set working dir to {}, errno={}", workingDir, errno);
}
}
if (logOutput) {
close(fildes[0]); // close the reading end of the pipe
dup2(fildes[1], STDOUT_FILENO); // redirect stdout to pipe
dup2(STDOUT_FILENO, STDERR_FILENO); // redirect stderr to stdout
}
ret = execv("/bin/sh", (char **)cmd);
if (ret < 0) {
LOG(Warning, " failed, errno={}", errno);
}
} else {
// parent process
LOG(Info, "{} started, pid={}", cmdLine, pid);
if (waitForEnd) {
int stat_loc;
if (logOutput) {
char lineBuffer[1024];
close(fildes[1]); // close the writing end of the pipe
FILE *stdPipe = fdopen(fildes[0], "r");
while (fgets(lineBuffer, sizeof(lineBuffer), stdPipe) != NULL) {
char *p = lineBuffer + strlen(lineBuffer)-1;
if (*p == '\n') *p=0;
Log::Logger::Write(LogType::Info, String(lineBuffer));
}
}
if (waitpid(pid, &stat_loc, 0) < 0) {
LOG(Warning, "waiting for pid {} failed, errno={}", pid, errno);
returnCode = errno;
} else {
if (WIFEXITED(stat_loc)) {
int error = WEXITSTATUS(stat_loc);
if (error != 0) {
LOG(Warning, "command exited with error code={}", error);
returnCode = error;
}
} else if (WIFSIGNALED(stat_loc)) {
LOG(Warning, "command was killed by signal#{}", WTERMSIG(stat_loc));
returnCode = EPIPE;
} else if (WIFSTOPPED(stat_loc)) {
LOG(Warning, "command was stopped by signal#{}", WSTOPSIG(stat_loc));
returnCode = EPIPE;
}
}
}
}
return returnCode;
}
// this will not log output by default
int32 LinuxPlatform::StartProcess(const StringView& filename, const StringView& args, const StringView& workingDir, bool hiddenWindow, bool waitForEnd)
{
String command(filename);
if (args.HasChars())
command += TEXT(" ") + String(args);
LOG(Info, "Command: {0}", command);
if (workingDir.HasChars())
{
LOG(Info, "Working directory: {0}", workingDir);
}
// TODO: support workingDir
// TODO: support hiddenWindow
// TODO: support waitForEnd
StringAnsi commandAnsi(command);
system(commandAnsi.GetText());
return 0;
// hiddenWindow has hardly any meaning on UNIX/Linux/OSX as the program that is called decides whether it has a GUI or not
String cmdLine(filename);
if (args.HasChars()) cmdLine = cmdLine + TEXT(" ") + args;
return LinuxProcess(cmdLine, workingDir, Dictionary<String, String>(), waitForEnd, false);
}
// this will log output
int32 LinuxPlatform::RunProcess(const StringView& cmdLine, const StringView& workingDir, bool hiddenWindow)
{
return RunProcess(cmdLine, workingDir, Dictionary<String, String>(), hiddenWindow);
return LinuxProcess(cmdLine, workingDir, Dictionary<String, String>(), true, true);
}
// this will log output
int32 LinuxPlatform::RunProcess(const StringView& cmdLine, const StringView& workingDir, const Dictionary<String, String>& environment, bool hiddenWindow)
{
LOG(Info, "Command: {0}", cmdLine);
if (workingDir.HasChars())
{
LOG(Info, "Working directory: {0}", workingDir);
}
// TODO: support environment
// TODO: support hiddenWindow
String dir = GetWorkingDirectory();
StringAnsi cmdLineAnsi;
if (workingDir.HasChars())
{
cmdLineAnsi += "cd \"";
cmdLineAnsi += StringAnsi(workingDir);
cmdLineAnsi += "\"; ";
}
cmdLineAnsi += StringAnsi(cmdLine);
FILE* pipe = popen(cmdLineAnsi.GetText(), "r");
if (!pipe)
{
LOG(Warning, "Cannot start process '{0}'", cmdLine);
return 1;
}
char rawData[256];
StringAnsi pathAnsi;
Array<Char> logData;
while (fgets(rawData, sizeof(rawData), pipe) != NULL)
{
logData.Clear();
int32 rawDataLength = StringUtils::Length(rawData);
if (rawDataLength == 0)
continue;
if (rawData[rawDataLength - 1] == '\0')
rawDataLength--;
if (rawData[rawDataLength - 1] == '\n')
rawDataLength--;
logData.Resize(rawDataLength + 1);
StringUtils::ConvertANSI2UTF16(rawData, logData.Get(), rawDataLength);
logData.Last() = '\0';
Log::Logger::Write(LogType::Info, StringView(logData.Get(), rawDataLength));
}
int32 result = pclose(pipe);
return result;
return LinuxProcess(cmdLine, workingDir, environment, true, true);
}
void* LinuxPlatform::LoadLibrary(const Char* filename)