Files
UnrealEngine/Engine/Source/Programs/IOS/iPhonePackager/SSHCommandHelper.cs
2025-05-18 13:04:45 +08:00

385 lines
10 KiB
C#

/**
* Copyright Epic Games, Inc. All Rights Reserved.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace iPhonePackager
{
public partial class SSHCommandHelper
{
private static string SSHExePath = "";
private static string SSHAuthentication = "";
private static string SSHUser = "";
private static string RSyncExePath = "";
/**
* Parse the commandline for SSH arguments
*/
public static bool ParseSSHProperties(string[] Arguments)
{
Int32 ArgIndex = 0;
foreach( string NextArg in Arguments)
{
string Arg = NextArg.ToLowerInvariant();
if (Arg.StartsWith("-"))
{
if (Arg == "-usessh")
{
Config.bUseRPCUtil = false;
}
else if (Arg == "-sshexe")
{
if (Arguments.Length > ArgIndex + 1)
{
SSHExePath = Arguments[ArgIndex+1];
}
else
{
return false;
}
}
else if (Arg == "-sshauth")
{
if (Arguments.Length > ArgIndex + 1)
{
SSHAuthentication = Arguments[ArgIndex + 1];
}
else
{
return false;
}
}
else if (Arg == "-sshuser")
{
if (Arguments.Length > ArgIndex + 1)
{
SSHUser = Arguments[ArgIndex + 1];
}
else
{
return false;
}
}
else if (Arg == "-rsyncexe")
{
if (Arguments.Length > ArgIndex + 1)
{
RSyncExePath = Arguments[ArgIndex + 1];
}
else
{
return false;
}
}
else if (Arg == "-overridedevroot")
{
if (Arguments.Length > ArgIndex + 1)
{
Config.OverrideDevRoot = Arguments[ArgIndex + 1];
}
else
{
return false;
}
}
}
++ArgIndex;
}
bool bValidSetup = true;
if (Config.bUseRPCUtil == false)
{
// If we aren't using rpc utility,
// ensure we have all the arguments needed for our ssh and rsync commands to run
bValidSetup = ValidateSetup();
}
return bValidSetup;
}
private static bool ValidateSetup()
{
bool bIsSetupForSSH = true;
if( string.IsNullOrEmpty(SSHExePath) )
{
Console.WriteLine("SSH requires the -sshexe commandline parameter");
bIsSetupForSSH = false;
}
else if (string.IsNullOrEmpty(SSHAuthentication))
{
Console.WriteLine("SSH requires the -sshauth commandline parameter");
bIsSetupForSSH = false;
}
else if (string.IsNullOrEmpty(SSHUser))
{
Console.WriteLine("SSH requires the -sshuser commandline parameter");
bIsSetupForSSH = false;
}
else if (string.IsNullOrEmpty(RSyncExePath))
{
Console.WriteLine("SSH requires the -rsyncexe commandline parameter");
bIsSetupForSSH = false;
}
else if (string.IsNullOrEmpty(Config.OverrideDevRoot))
{
Console.WriteLine("For now, SSH requires the -overridedevroot commandline parameter");
bIsSetupForSSH = false;
}
return bIsSetupForSSH;
}
// SSH command output, tracks output and errors from the remote execution
private static Dictionary<Object, StringBuilder> SSHOutputMap = new Dictionary<object, StringBuilder>();
public static Hashtable SSHReturn = new Hashtable();
/**
* Execute a console command on the remote mac.
*/
static public bool Command(string RemoteMac, string Command, string WorkingFolder)
{
// make the commandline for other end
string RemoteCommandline = "cd " + WorkingFolder + " && " + Command;
Process SSHProcess = new Process();
// Get the executable dir for SSH
string ExeDir = Path.GetDirectoryName(SSHExePath);
if (ExeDir != "")
{
SSHProcess.StartInfo.WorkingDirectory = ExeDir;
}
SSHProcess.StartInfo.FileName = SSHExePath;
SSHProcess.StartInfo.Arguments = string.Format(
"{0} \"{1}@{2}\" \"{3}\"",
SSHAuthentication,
SSHUser,
RemoteMac,
RemoteCommandline.Replace("\"", "\\\""));
// add this process to the map
SSHOutputMap[SSHProcess] = new StringBuilder("");
SSHProcess.StartInfo.UseShellExecute = false;
SSHProcess.StartInfo.RedirectStandardOutput = true;
SSHProcess.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedForSSH);
SSHProcess.StartInfo.RedirectStandardError = true;
SSHProcess.ErrorDataReceived += new DataReceivedEventHandler(OutputReceivedForSSH);
DateTime Start = DateTime.Now;
SSHProcess.Start();
SSHProcess.BeginOutputReadLine();
SSHProcess.BeginErrorReadLine();
SSHProcess.WaitForExit();
Console.WriteLine("Execute took {0}", (DateTime.Now - Start).ToString());
// now we have enough to fill out the HashTable
SSHReturn = new Hashtable();
SSHReturn["CommandOutput"] = SSHOutputMap[SSHProcess].ToString();
SSHReturn["ExitCode"] = (object)SSHProcess.ExitCode;
SSHOutputMap.Remove(SSHProcess);
return SSHProcess.ExitCode == 0;
}
/**
* Upload a number of files to the remote mac
*/
public static void BatchUpload(string RemoteMac, string[] CopyCommands)
{
// make a new structure to send over the wire with local filetime, and remote filename
List<string> LocalNames = new List<string>();
List<string> RemoteNames = new List<string>();
foreach (string Pair in CopyCommands)
{
// each pair is a local and remote filename
string[] Filenames = Pair.Split(";".ToCharArray());
if (Filenames.Length == 2)
{
FileInfo Info = new FileInfo(Filenames[0]);
if (Info.Exists)
{
// make the remote filename -> local filetime pair
LocalNames.Add(Filenames[0]);
RemoteNames.Add(Filenames[1]);
}
else
{
Console.WriteLine("Tried to batch upload non-existant file: " + Filenames[0]);
}
}
}
for (int Index = 0; Index < LocalNames.Count; Index++)
{
// now send it over
Hashtable Result = UploadFile(RemoteMac, LocalNames[Index], RemoteNames[Index]);
if (Result == null)
{
// we want to fail here so builds don't quietly fail, but we use a useful message
throw new Exception(string.Format("Failed to upload local file {0} to {1}", LocalNames[Index], RemoteNames[Index]));
}
}
}
/**
* Upload a single file to the remote mac
*/
static public Hashtable UploadFile(string RemoteMac, string LocalPath, string RemotePath)
{
string RemoteDir = Path.GetDirectoryName(RemotePath).Replace("\\", "/");
RemoteDir = RemoteDir.Replace(" ", "\\ ");
string RemoteFilename = Path.GetFileName(RemotePath);
// get the executable dir for SSH, so Rsync can call it easily
string ExeDir = Path.GetDirectoryName(SSHExePath);
Process RsyncProcess = new Process();
if (ExeDir != "")
{
RsyncProcess.StartInfo.WorkingDirectory = ExeDir;
}
// make simple rsync commandline to send a file
RsyncProcess.StartInfo.FileName = RSyncExePath;
RsyncProcess.StartInfo.Arguments = string.Format(
"-zrltgoDe \"./ssh {0}\" --rsync-path=\"mkdir -p {1} && rsync\" --chmod=ug=rwX,o=rxX '{2}' \"{3}@{4}\":'{1}/{5}'",
SSHAuthentication,
RemoteDir,
ConvertPathToCygwin(LocalPath),
SSHUser,
RemoteMac,
RemoteFilename
);
SSHOutputMap[RsyncProcess] = new StringBuilder("");
RsyncProcess.StartInfo.UseShellExecute = false;
RsyncProcess.StartInfo.RedirectStandardOutput = true;
RsyncProcess.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedForRsync);
RsyncProcess.StartInfo.RedirectStandardError = true;
RsyncProcess.ErrorDataReceived += new DataReceivedEventHandler(OutputReceivedForRsync);
RsyncProcess.Start();
RsyncProcess.BeginOutputReadLine();
RsyncProcess.BeginErrorReadLine();
RsyncProcess.WaitForExit();
// now we have enough to fill out the HashTable
Hashtable ReturnHash = new Hashtable();
ReturnHash["CommandOutput"] = SSHOutputMap[RsyncProcess].ToString();
ReturnHash["ExitCode"] = (object)RsyncProcess.ExitCode;
// run rsync (0 means success)
return ReturnHash;
}
/**
* Download a file from the remote mac
*/
static public Hashtable DownloadFile(string RemoteMac, string RemotePath, string LocalPath)
{
// get the executable dir for SSH, so Rsync can call it easily
string ExeDir = Path.GetDirectoryName(SSHExePath);
string RemoteDir = RemotePath.Replace("\\", "/");
RemoteDir = RemoteDir.Replace(" ", "\\ ");
Process RsyncProcess = new Process();
if (ExeDir != "")
{
RsyncProcess.StartInfo.WorkingDirectory = ExeDir;
}
// make sure directory exists to download to
Directory.CreateDirectory(Path.GetDirectoryName(LocalPath));
// make simple rsync commandline to send a file
RsyncProcess.StartInfo.FileName = RSyncExePath;
RsyncProcess.StartInfo.Arguments = string.Format(
"-zrltgoDe \"./ssh {0}\" \"{2}@{3}\":'{4}' \"{1}\"",
SSHAuthentication,
ConvertPathToCygwin(LocalPath),
SSHUser,
RemoteMac,
RemoteDir
);
SSHOutputMap[RsyncProcess] = new StringBuilder("");
RsyncProcess.StartInfo.UseShellExecute = false;
RsyncProcess.StartInfo.RedirectStandardOutput = true;
RsyncProcess.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedForRsync);
RsyncProcess.StartInfo.RedirectStandardError = true;
RsyncProcess.ErrorDataReceived += new DataReceivedEventHandler(OutputReceivedForRsync);
RsyncProcess.Start();
RsyncProcess.BeginOutputReadLine();
RsyncProcess.BeginErrorReadLine();
RsyncProcess.WaitForExit();
// now we have enough to fill out the HashTable
Hashtable ReturnHash = new Hashtable();
ReturnHash["CommandOutput"] = SSHOutputMap[RsyncProcess].ToString();
ReturnHash["ExitCode"] = (Int64)RsyncProcess.ExitCode;
// run rsync (0 means success)
return ReturnHash;
}
static public void OutputReceivedForRsync(Object Sender, DataReceivedEventArgs Line)
{
if ((Line != null) && (Line.Data != null) && (Line.Data != ""))
{
Program.Log(Line.Data);
}
}
static public void OutputReceivedForSSH(Object Sender, DataReceivedEventArgs Line)
{
if ((Line != null) && (Line.Data != null) && (Line.Data != ""))
{
Program.Log(Line.Data);
StringBuilder SSHOutput = SSHOutputMap[Sender];
if (SSHOutput.Length != 0)
{
SSHOutput.Append(Environment.NewLine);
}
SSHOutput.Append(Line.Data);
}
}
private static string ConvertPathToCygwin(string InPath)
{
if (InPath == null)
{
return null;
}
return "/cygdrive/" + InPath.Replace("\\\\", "/").Replace("\\", "/").Replace(":", "");
}
}
}