Example #1
0
    static public (ProcessOutput processOutput, IReadOnlyCollection <(IImmutableList <string> path, IReadOnlyList <byte> content)> resultingFiles) ExecuteFileWithArguments(
        IReadOnlyDictionary <IImmutableList <string>, IReadOnlyList <byte> > environmentFilesNotExecutable,
        byte[] executableFile,
        string arguments,
        IDictionary <string, string>?environmentStrings,
        IImmutableList <string>?workingDirectory = null,
        IReadOnlyDictionary <IImmutableList <string>, IReadOnlyList <byte> >?environmentFilesExecutable = null,
        IReadOnlyDictionary <string, IReadOnlyList <byte> >?environmentPathExecutableFiles = null)
    {
        var environmentStringsDict =
            environmentStrings?.ToImmutableDictionary() ?? ImmutableDictionary <string, string> .Empty;

        var environmentPathContainerDirectoryName = "environment-path-cont";

        var containerDirectory = Filesystem.CreateRandomDirectoryInTempDirectory();

        string writeEnvironmentFile(KeyValuePair <IImmutableList <string>, IReadOnlyList <byte> > environmentFile)
        {
            var environmentFilePath      = Path.Combine(containerDirectory, Filesystem.MakePlatformSpecificPath(environmentFile.Key));
            var environmentFileDirectory = Path.GetDirectoryName(environmentFilePath) !;

            Directory.CreateDirectory(environmentFileDirectory);

            File.WriteAllBytes(environmentFilePath, (environmentFile.Value as byte[]) ?? environmentFile.Value.ToArray());

            return(environmentFilePath);
        }

        foreach (var environmentFile in environmentFilesNotExecutable)
        {
            writeEnvironmentFile(environmentFile);
        }

        var mainExecutableFileName         = "name-used-to-execute-file.exe";
        var mainExecutableFilePathRelative = ImmutableList.Create(mainExecutableFileName);

        var executableFileNameAppendix =
            RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";

        var allExecutableFiles =
            (environmentFilesExecutable ?? ImmutableDictionary <IImmutableList <string>, IReadOnlyList <byte> > .Empty)
            .ToImmutableDictionary()
            .SetItems(
                (environmentPathExecutableFiles ?? ImmutableDictionary <string, IReadOnlyList <byte> > .Empty)
                .Select(execFile => new KeyValuePair <IImmutableList <string>, IReadOnlyList <byte> >(
                            ImmutableList.Create(environmentPathContainerDirectoryName, execFile.Key + executableFileNameAppendix), execFile.Value)))
            .SetItem(mainExecutableFilePathRelative, executableFile);

        foreach (var environmentFile in allExecutableFiles)
        {
            var fileAbsolutePath = writeEnvironmentFile(environmentFile);

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                var unixFileInfo = new UnixFileInfo(fileAbsolutePath);

                unixFileInfo.FileAccessPermissions |=
                    FileAccessPermissions.GroupExecute | FileAccessPermissions.UserExecute | FileAccessPermissions.OtherExecute |
                    FileAccessPermissions.GroupRead | FileAccessPermissions.UserRead | FileAccessPermissions.OtherRead;
            }
        }

        var workingDirectoryAbsolute =
            Path.Combine(
                containerDirectory,
                Filesystem.MakePlatformSpecificPath(workingDirectory ?? ImmutableList <string> .Empty));

        var mainExecutableFilePathAbsolute = Path.Combine(containerDirectory, mainExecutableFileName);

        var environmentPathExecutableFilesPathAbsolute = Path.Combine(containerDirectory, environmentPathContainerDirectoryName);

        var pathEnvironmentVarSeparator =
            RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":";

        var environmentPathEntryBefore =
            environmentStringsDict.FirstOrDefault(c => c.Key.Equals("PATH", StringComparison.InvariantCultureIgnoreCase));

        var environmentPath = environmentPathExecutableFilesPathAbsolute + pathEnvironmentVarSeparator + environmentPathEntryBefore.Value;

        var environmentStringsWithExecutableFiles =
            environmentStringsDict
            .SetItem(environmentPathEntryBefore.Key ?? "PATH", environmentPath);

        var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                WorkingDirectory       = workingDirectoryAbsolute,
                FileName               = mainExecutableFilePathAbsolute,
                Arguments              = arguments,
                UseShellExecute        = false,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                CreateNoWindow         = true,
            },
        };

        foreach (var envString in environmentStringsWithExecutableFiles.EmptyIfNull())
        {
            process.StartInfo.Environment[envString.Key] = envString.Value;
        }

        process.Start();
        var standardOutput = process.StandardOutput.ReadToEnd();
        var standardError  = process.StandardError.ReadToEnd();

        process.WaitForExit();
        var exitCode = process.ExitCode;

        process.Close();

        var createdFiles =
            Filesystem.GetFilesFromDirectory(
                directoryPath: containerDirectory,
                filterByRelativeName: path => !path.SequenceEqual(mainExecutableFilePathRelative));

        try
        {
            Directory.Delete(path: containerDirectory, recursive: true);
        }
        // Avoid crash in scenario like https://forum.botlab.org/t/farm-manager-tribal-wars-2-farmbot/3038/170
        catch (UnauthorizedAccessException)
        {
        }

        return(new ProcessOutput
        {
            ExitCode = exitCode,
            StandardError = standardError,
            StandardOutput = standardOutput,
        }, createdFiles);
    }