1 //
2 // Copyright (c) 2010-2025 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.IO;
10 using System.Linq;
11 using System.Diagnostics;
12 using Antmicro.Renode.Core;
13 
14 namespace Antmicro.Renode.Utilities
15 {
16     public class TemporaryFilesManager
17     {
TemporaryFilesManager()18         static TemporaryFilesManager()
19         {
20             Initialize(Path.GetTempPath(), DefaultDirectoryPrefix, !EmulationManager.DisableEmulationFilesCleanup);
21         }
22 
23         public static TemporaryFilesManager Instance { get; private set; }
24 
Initialize(string tempDirectory, string tempDirPrefix, bool cleanFiles)25         public static void Initialize(string tempDirectory, string tempDirPrefix, bool cleanFiles)
26         {
27             Instance = new TemporaryFilesManager(tempDirectory, tempDirPrefix, cleanFiles);
28         }
29 
GetTemporaryFile(string fileNameSuffix = null)30         public string GetTemporaryFile(string fileNameSuffix = null)
31         {
32             lock(emulatorTemporaryPath)
33             {
34                 string path;
35                 do
36                 {
37                     var fileName = string.Format(fileNameSuffix != null ? $"{Guid.NewGuid()}-{fileNameSuffix}" : $"{Guid.NewGuid()}.tmp");
38                     path = Path.Combine(emulatorTemporaryPath, fileName);
39                     // this is guid, collision is very unlikely
40                 }
41                 while(File.Exists(path));
42 
43                 using(File.Create(path))
44                 {
45                     //that's the simplest way to create and NOT have the file open
46                 }
47 
48                 var ofc = OnFileCreated;
49                 if(ofc != null)
50                 {
51                     ofc(path);
52                 }
53 
54                 return path;
55             }
56         }
57 
TryCreateFile(string fileName, out string path, bool overwriteExistingFile = false)58         public bool TryCreateFile(string fileName, out string path, bool overwriteExistingFile = false)
59         {
60             path = Path.Combine(emulatorTemporaryPath, fileName);
61 
62             try
63             {
64                 if(File.Exists(path))
65                 {
66                     if(!overwriteExistingFile)
67                     {
68                         path = null;
69                         return false;
70                     }
71                     File.Delete(path);
72                 }
73 
74                 using(File.Create(path))
75                 {
76                     //that's the simplest way to create and NOT have the file open
77                 }
78             }
79             catch(Exception)
80             {
81                 path = null;
82                 return false;
83             }
84 
85             return true;
86         }
87 
Cleanup()88         public void Cleanup()
89         {
90             if(!shouldCleanFiles)
91             {
92                 return;
93             }
94 
95             foreach(var entry in Directory.GetDirectories(Directory.GetParent(emulatorTemporaryPath).FullName)
96                 .Where(x => x != emulatorTemporaryPath && x.StartsWith(otherEmulatorTempPrefix, StringComparison.Ordinal)
97                     && !x.EndsWith(CrashSuffix, StringComparison.Ordinal)))
98             {
99                 var pid = entry.Substring(otherEmulatorTempPrefix.Length);
100                 int processId;
101                 if(pid != null && int.TryParse(pid, out processId) && IsProcessAlive(processId))
102                 {
103                     continue;
104                 }
105                 ClearDirectory(entry);
106             }
107         }
108 
109         public event Action<string> OnFileCreated;
110 
111         public string EmulatorTemporaryPath
112         {
113             get
114             {
115                 return emulatorTemporaryPath;
116             }
117         }
118 
TemporaryFilesManager(string tempDirectory, string tempDirPrefix, bool cleanFiles)119         private TemporaryFilesManager(string tempDirectory, string tempDirPrefix, bool cleanFiles)
120         {
121             shouldCleanFiles = cleanFiles;
122             if(AppDomain.CurrentDomain.IsDefaultAppDomain())
123             {
124                 id = Process.GetCurrentProcess().Id.ToString();
125             }
126             else
127             {
128                 id = string.Format("{0}-{1}", Process.GetCurrentProcess().Id, AppDomain.CurrentDomain.Id);
129             }
130             otherEmulatorTempPrefix = Path.Combine(tempDirectory, tempDirPrefix);
131             emulatorTemporaryPath = otherEmulatorTempPrefix + id;
132 
133             if(!Directory.Exists(emulatorTemporaryPath))
134             {
135                 Directory.CreateDirectory(emulatorTemporaryPath);
136             }
137             Cleanup();
138         }
139 
IsProcessAlive(int pid)140         private static bool IsProcessAlive(int pid)
141         {
142             try
143             {
144                 var proc = Process.GetProcessById(pid);
145                 return proc != null && !proc.HasExited;
146             }
147             catch(ArgumentException)
148             {
149                 return false;
150             }
151         }
152 
~TemporaryFilesManager()153         ~TemporaryFilesManager()
154         {
155             Cleanup();
156             ClearDirectory(emulatorTemporaryPath);
157         }
158 
ClearDirectory(string path)159         private void ClearDirectory(string path)
160         {
161             if(!shouldCleanFiles)
162             {
163                 return;
164             }
165             try
166             {
167                 Directory.Delete(path, true);
168             }
169             catch(Exception)
170             {
171                 // we did everything we could
172             }
173         }
174 
175         public const string CrashSuffix = "-crash";
176 
177         private readonly string otherEmulatorTempPrefix;
178         private readonly string emulatorTemporaryPath;
179         private readonly string id;
180         private readonly bool shouldCleanFiles;
181 
182         private const string DefaultDirectoryPrefix = "renode-";
183     }
184 }
185