diff --git a/hanatui/publish/hanatui b/hanatui/publish/hanatui index 35a9bf1..0b0accc 100755 Binary files a/hanatui/publish/hanatui and b/hanatui/publish/hanatui differ diff --git a/hanatui/src/Hana/HdbCliRunner.cs b/hanatui/src/Hana/HdbCliRunner.cs index ab49ba4..05a9eac 100644 --- a/hanatui/src/Hana/HdbCliRunner.cs +++ b/hanatui/src/Hana/HdbCliRunner.cs @@ -13,28 +13,19 @@ public sealed class HdbCliRunner /// public static List ListKeys() { - var result = RunAndCapture(HdbClientLocator.HdbUserstore, "list"); + var result = RunAndCapture(HdbClientLocator.HdbUserstore, ["list"]); if (!result.Success) return []; - return HdbUserstoreKey.ParseFrom(result.Output); } - /// - /// Tests a key connection by running a trivial SELECT against DUMMY. - /// Returns true on success. - /// public static bool TestKey(string key) { - var result = RunAndCapture( - HdbClientLocator.HdbSql, - $"-U \"{key}\" \"SELECT 'ok' FROM DUMMY\""); + var result = RunAndCapture(HdbClientLocator.HdbSql, + ["-U", key, "SELECT 'ok' FROM DUMMY"]); return result.Success && result.Output.Contains("ok"); } - /// - /// Lists eligible schemas for the given userstore key. - /// public static (bool Success, List Schemas, string Error) ListSchemas(string key) { const string query = @@ -42,7 +33,7 @@ public sealed class HdbCliRunner "WHERE SCHEMA_OWNER = 'SYSTEM' " + "AND SCHEMA_NAME NOT IN ('_SYS_SECURITY', 'IFSERV', 'B1if', 'SYSTEM', 'RSP');"; - var result = RunAndCapture(HdbClientLocator.HdbSql, $"-U \"{key}\" \"{query}\""); + var result = RunAndCapture(HdbClientLocator.HdbSql, ["-U", key, query]); if (!result.Success) return (false, [], result.Output); @@ -73,9 +64,11 @@ public sealed class HdbCliRunner Action onOutputLine, CancellationToken ct) { + // Use ArgumentList (not Arguments) so args are passed directly to the process + // without shell quoting — double quotes inside SQL won't break the argument boundary. return await RunStreamingAsync( HdbClientLocator.HdbSql, - $"-U \"{key}\" \"{sql}\"", + ["-U", key, sql], onOutputLine, ct); } @@ -90,23 +83,29 @@ public sealed class HdbCliRunner Action onOutputLine, CancellationToken ct) { - return await RunStreamingAsync(executable, arguments, onOutputLine, ct); + // Shell commands (tar, pigz) need shell parsing for things like -I "pigz -p N", + // so we use a bash -c wrapper to ensure correct tokenisation. + return await RunStreamingAsync( + "/bin/bash", + ["-c", arguments], + onOutputLine, + ct); } // ------------------------------------------------------------------------- - private static (bool Success, string Output) RunAndCapture(string exe, string args) + private static (bool Success, string Output) RunAndCapture(string exe, string[] args) { try { var psi = new ProcessStartInfo(exe) { - Arguments = args, RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, }; + foreach (var a in args) psi.ArgumentList.Add(a); using var proc = Process.Start(psi); if (proc is null) @@ -116,10 +115,9 @@ public sealed class HdbCliRunner var stderr = proc.StandardError.ReadToEnd(); proc.WaitForExit(); - if (proc.ExitCode == 0) - return (true, stdout); - else - return (false, string.IsNullOrWhiteSpace(stderr) ? stdout : stderr); + return proc.ExitCode == 0 + ? (true, stdout) + : (false, string.IsNullOrWhiteSpace(stderr) ? stdout : stderr); } catch (Exception ex) { @@ -129,18 +127,18 @@ public sealed class HdbCliRunner private static async Task RunStreamingAsync( string exe, - string args, + string[] args, Action onOutputLine, CancellationToken ct) { var psi = new ProcessStartInfo(exe) { - Arguments = args, RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, }; + foreach (var a in args) psi.ArgumentList.Add(a); Process? proc; try diff --git a/hanatui/src/Hana/SchemaService.cs b/hanatui/src/Hana/SchemaService.cs index b56d269..80e1ee2 100644 --- a/hanatui/src/Hana/SchemaService.cs +++ b/hanatui/src/Hana/SchemaService.cs @@ -357,7 +357,7 @@ public sealed class SchemaService(string userKey) private async Task GetTenantNameAsync() { var sql = SqlQueryBuilder.GetTenantName(); - var (success, output) = RunCapture(HdbClientLocator.HdbSql, $"-U \"{_key}\" \"{sql}\""); + var (success, output) = RunCapture(HdbClientLocator.HdbSql, ["-U", _key, sql]); if (!success) return ""; foreach (var line in output.Split('\n')) @@ -373,19 +373,19 @@ public sealed class SchemaService(string userKey) return ""; } - private static (bool, string) RunCapture(string exe, string args) + private static (bool, string) RunCapture(string exe, string[] args) { try { - var psi = new SysDiag.ProcessStartInfo(exe) - { - Arguments = args, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, - }; - using var proc = SysDiag.Process.Start(psi); + var psi = new SysDiag.ProcessStartInfo(exe) + { + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + foreach (var a in args) psi.ArgumentList.Add(a); + using var proc = SysDiag.Process.Start(psi); if (proc is null) return (false, ""); var stdout = proc.StandardOutput.ReadToEnd(); var stderr = proc.StandardError.ReadToEnd();