Skip to content

Commit 1ee32e3

Browse files
committed
startup-launcher: drop support for timeout
1 parent d1ccf9d commit 1ee32e3

3 files changed

Lines changed: 110 additions & 145 deletions

File tree

BrowserGuard.Tests/ConfigLoaderTests.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ public void ParsesStartupPrograms()
9696
"Arguments": ["--mode", "kiosk"],
9797
"WorkingDirectory": "C:\\Program Files\\Contoso",
9898
"EnvironmentVariables": { "CONTOSO_PROFILE": "default", "LANG": "ja" },
99-
"TimeoutSeconds": 30,
10099
"Sha256": "abc123"
101100
},
102101
{
@@ -118,7 +117,6 @@ public void ParsesStartupPrograms()
118117
Assert.Equal(@"C:\Program Files\Contoso", first.WorkingDirectory);
119118
Assert.Equal("default", first.EnvironmentVariables["CONTOSO_PROFILE"]);
120119
Assert.Equal("ja", first.EnvironmentVariables["LANG"]);
121-
Assert.Equal(30, first.TimeoutSeconds);
122120
Assert.Equal("abc123", first.Sha256);
123121

124122
// Everything but the path may be left out.
@@ -127,7 +125,6 @@ public void ParsesStartupPrograms()
127125
Assert.Empty(second.Arguments);
128126
Assert.Equal("", second.WorkingDirectory);
129127
Assert.Empty(second.EnvironmentVariables);
130-
Assert.Equal(0, second.TimeoutSeconds);
131128
Assert.Equal("", second.Sha256);
132129
}
133130

BrowserGuard/ConfigLoader.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ internal class StartupProgramConfig
7474
public string[] Arguments { get; set; } = [];
7575
public string WorkingDirectory { get; set; } = "";
7676
public Dictionary<string, string> EnvironmentVariables { get; set; } = new();
77-
public int TimeoutSeconds { get; set; }
7877
public string Sha256 { get; set; } = "";
7978
}
8079

BrowserGuard/StartupLauncher.cs

Lines changed: 110 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,141 +1,110 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Diagnostics;
4-
using System.IO;
5-
using System.Security.Cryptography;
6-
7-
namespace BrowserGuard
8-
{
9-
internal static class StartupLauncher
10-
{
11-
internal static string? Run(StartupLauncherConfig config, Logger? logger = null)
12-
{
13-
if (!config.Enabled)
14-
{
15-
logger?.Log("StartupLauncher is disabled");
16-
return null;
17-
}
18-
19-
var failures = new List<string>();
20-
foreach (var program in config.Programs)
21-
{
22-
var failure = Start(program, logger);
23-
if (failure is not null)
24-
{
25-
logger?.Log($"StartupLauncher: {failure}");
26-
failures.Add(failure);
27-
}
28-
else
29-
{
30-
logger?.Log($"StartupLauncher started {program.Path}");
31-
}
32-
}
33-
34-
return failures.Count == 0 ? null : string.Join("; ", failures);
35-
}
36-
37-
private static string? Start(StartupProgramConfig program, Logger? logger)
38-
{
39-
var rejection = Verify(program);
40-
if (rejection is not null)
41-
{
42-
return rejection;
43-
}
44-
45-
try
46-
{
47-
using var process = Process.Start(BuildStartInfo(program))
48-
?? throw new InvalidOperationException("no process was started");
49-
return WaitUntilStarted(process, program, logger);
50-
}
51-
catch (Exception ex)
52-
{
53-
return $"{program.Path}: {ex.Message}";
54-
}
55-
}
56-
57-
internal static string? Verify(StartupProgramConfig program)
58-
{
59-
if (string.IsNullOrWhiteSpace(program.Path))
60-
{
61-
return "a program without a path was configured";
62-
}
63-
if (!File.Exists(program.Path))
64-
{
65-
return $"{program.Path}: not found";
66-
}
67-
if (string.IsNullOrWhiteSpace(program.Sha256))
68-
{
69-
return null;
70-
}
71-
72-
var actual = ComputeSha256(program.Path);
73-
if (!actual.Equals(program.Sha256.Trim(), StringComparison.OrdinalIgnoreCase))
74-
{
75-
return $"{program.Path}: hash mismatch, expected {program.Sha256} but found {actual}";
76-
}
77-
return null;
78-
}
79-
80-
internal static string ComputeSha256(string path)
81-
{
82-
using var stream = File.OpenRead(path);
83-
return Convert.ToHexString(SHA256.HashData(stream)).ToLowerInvariant();
84-
}
85-
86-
internal static ProcessStartInfo BuildStartInfo(StartupProgramConfig program)
87-
{
88-
// UseShellExecute has to be off for the environment to be passed on.
89-
var info = new ProcessStartInfo
90-
{
91-
FileName = program.Path,
92-
UseShellExecute = false,
93-
WorkingDirectory = string.IsNullOrWhiteSpace(program.WorkingDirectory)
94-
? Path.GetDirectoryName(Path.GetFullPath(program.Path)) ?? ""
95-
: program.WorkingDirectory,
96-
};
97-
98-
foreach (var argument in program.Arguments)
99-
{
100-
info.ArgumentList.Add(argument);
101-
}
102-
foreach (var variable in program.EnvironmentVariables)
103-
{
104-
info.Environment[variable.Key] = variable.Value;
105-
}
106-
107-
return info;
108-
}
109-
110-
111-
/// <summary>
112-
/// Wait until the program is ready to receive input, or until the timeout expires.
113-
/// TimeoutSeconds bounds how long we wait for the program to finish starting, not how long it may run.
114-
/// </summary>
115-
/// <param name="process"></param>
116-
/// <param name="program"></param>
117-
/// <param name="logger"></param>
118-
/// <returns></returns>
119-
private static string? WaitUntilStarted(Process process, StartupProgramConfig program, Logger? logger)
120-
{
121-
if (program.TimeoutSeconds <= 0)
122-
{
123-
return null;
124-
}
125-
126-
try
127-
{
128-
if (!process.WaitForInputIdle(program.TimeoutSeconds * 1000))
129-
{
130-
return $"{program.Path}: still not ready after {program.TimeoutSeconds}s";
131-
}
132-
}
133-
catch (InvalidOperationException)
134-
{
135-
// No message loop, so there is no idle state to wait for.
136-
logger?.Log($"StartupLauncher: {program.Path} has no UI, not waiting");
137-
}
138-
return null;
139-
}
140-
}
141-
}
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Security.Cryptography;
6+
7+
namespace BrowserGuard
8+
{
9+
internal static class StartupLauncher
10+
{
11+
internal static string? Run(StartupLauncherConfig config, Logger? logger = null)
12+
{
13+
if (!config.Enabled)
14+
{
15+
logger?.Log("StartupLauncher is disabled");
16+
return null;
17+
}
18+
19+
var failures = new List<string>();
20+
foreach (var program in config.Programs)
21+
{
22+
var failure = Start(program);
23+
if (failure is not null)
24+
{
25+
logger?.Log($"StartupLauncher: {failure}");
26+
failures.Add(failure);
27+
}
28+
else
29+
{
30+
logger?.Log($"StartupLauncher started {program.Path}");
31+
}
32+
}
33+
34+
return failures.Count == 0 ? null : string.Join("; ", failures);
35+
}
36+
37+
private static string? Start(StartupProgramConfig program)
38+
{
39+
var rejection = Verify(program);
40+
if (rejection is not null)
41+
{
42+
return rejection;
43+
}
44+
45+
try
46+
{
47+
using var process = Process.Start(BuildStartInfo(program))
48+
?? throw new InvalidOperationException("no process was started");
49+
return null;
50+
}
51+
catch (Exception ex)
52+
{
53+
return $"{program.Path}: {ex.Message}";
54+
}
55+
}
56+
57+
internal static string? Verify(StartupProgramConfig program)
58+
{
59+
if (string.IsNullOrWhiteSpace(program.Path))
60+
{
61+
return "a program without a path was configured";
62+
}
63+
if (!File.Exists(program.Path))
64+
{
65+
return $"{program.Path}: not found";
66+
}
67+
if (string.IsNullOrWhiteSpace(program.Sha256))
68+
{
69+
return null;
70+
}
71+
72+
var actual = ComputeSha256(program.Path);
73+
if (!actual.Equals(program.Sha256.Trim(), StringComparison.OrdinalIgnoreCase))
74+
{
75+
return $"{program.Path}: hash mismatch, expected {program.Sha256} but found {actual}";
76+
}
77+
return null;
78+
}
79+
80+
internal static string ComputeSha256(string path)
81+
{
82+
using var stream = File.OpenRead(path);
83+
return Convert.ToHexString(SHA256.HashData(stream)).ToLowerInvariant();
84+
}
85+
86+
internal static ProcessStartInfo BuildStartInfo(StartupProgramConfig program)
87+
{
88+
// UseShellExecute has to be off for the environment to be passed on.
89+
var info = new ProcessStartInfo
90+
{
91+
FileName = program.Path,
92+
UseShellExecute = false,
93+
WorkingDirectory = string.IsNullOrWhiteSpace(program.WorkingDirectory)
94+
? Path.GetDirectoryName(Path.GetFullPath(program.Path)) ?? ""
95+
: program.WorkingDirectory,
96+
};
97+
98+
foreach (var argument in program.Arguments)
99+
{
100+
info.ArgumentList.Add(argument);
101+
}
102+
foreach (var variable in program.EnvironmentVariables)
103+
{
104+
info.Environment[variable.Key] = variable.Value;
105+
}
106+
107+
return info;
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)