1 //
2 // Copyright (c) 2010-2021 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 #if !PLATFORM_WINDOWS
8 using System;
9 using System.ComponentModel;
10 using System.Diagnostics;
11 using System.Threading;
12 using AntShell.Terminal;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.UI
17 {
18     public abstract class ProcessBasedProvider : IConsoleBackendAnalyzerProvider
19     {
20         // isMonitorWindows is not used for ProcessBasedProvider
TryOpen(string consoleName, out IIOSource io, bool isMonitorWindow = false)21         public bool TryOpen(string consoleName, out IIOSource io, bool isMonitorWindow = false)
22         {
23             var ptyUnixStream = new PtyUnixStream();
24             io = new StreamIOSource(ptyUnixStream);
25 
26             if(!CheckScreenTool())
27             {
28                 process = null;
29                 return false;
30             }
31 
32             var commandString = $"{ScreenTool} {(ptyUnixStream.SlaveName)}";
33             process = CreateProcess(consoleName, commandString);
34             if(!RunProcess(process))
35             {
36                 process = null;
37                 return false;
38             }
39 
40             // here we give 1s time for screen to start; otherwise some initial data (e.g. banner could be lost)
41             Thread.Sleep(1000);
42             return true;
43         }
44 
Close()45         public void Close()
46         {
47             var p = process;
48             if(p == null)
49             {
50                 return;
51             }
52 
53             try
54             {
55                 p.CloseMainWindow();
56             }
57             catch(InvalidOperationException e)
58             {
59                 // do not report an exception if the process has already exited
60                 if(!e.Message.Contains("finished") && !e.Message.Contains("exited"))
61                 {
62                     throw;
63                 }
64             }
65             process = null;
66         }
67 
68         public event Action OnClose;
69 
CreateProcess(string consoleName, string command)70         protected abstract Process CreateProcess(string consoleName, string command);
71 
LogError(string source, string arguments, int exitCode)72         protected void LogError(string source, string arguments, int exitCode)
73         {
74             Logger.LogAs(this, LogLevel.Error, "There was an error while starting {0} with arguments: {1}. It exited with code: {2}. In order to use different terminal, change preferences in configuration file.", source, arguments, exitCode);
75         }
76 
InnerOnClose()77         protected void InnerOnClose()
78         {
79             OnClose?.Invoke();
80         }
81 
RunProcess(Process p)82         private bool RunProcess(Process p)
83         {
84             try
85             {
86                 p.Start();
87                 return true;
88             }
89             catch(Win32Exception e)
90             {
91                 if(e.NativeErrorCode == 2)
92                 {
93                     Logger.LogAs(this, LogLevel.Warning, "Could not find binary: {0}", p.StartInfo.FileName);
94                 }
95                 else
96                 {
97                     Logger.LogAs(this, LogLevel.Error, "There was an error when starting process: {0}", e.Message);
98                 }
99             }
100 
101             return false;
102         }
103 
CheckScreenTool()104         private bool CheckScreenTool()
105         {
106             var p = new Process();
107             p.StartInfo = new ProcessStartInfo(ScreenTool, "--help")
108             {
109                 UseShellExecute = false,
110                 RedirectStandardError = true,
111                 RedirectStandardOutput = true,
112                 RedirectStandardInput = true,
113             };
114             return RunProcess(p);
115         }
116 
117         private Process process;
118 
119         private const string ScreenTool = "screen";
120     }
121 }
122 
123 #endif
124