FreeNOS
RecoveryServer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020 Niek Linnenbank
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <FreeNOS/System.h>
19 #include <Log.h>
20 #include <FileSystemClient.h>
21 #include <ExecutableFormat.h>
22 #include <Lz4Decompressor.h>
23 #include "RecoveryServer.h"
24 
27 {
29 }
30 
32 {
33  DEBUG("pid = " << msg->pid);
34 
35  // The identifier must be in range of allowed values
36  if (msg->pid >= MAX_PROCS)
37  {
38  ERROR("invalid PID " << msg->pid);
40  return;
41  }
42 
43  // Retrieve state information of the given process
44  ProcessInfo pinfo;
45  API::Result result = ProcessCtl(msg->pid, InfoPID, (Address) &pinfo);
46  if (result != API::Success)
47  {
48  ERROR("failed to retrieve ProcessInfo for PID " << msg->pid <<
49  ": result = " << (int)result);
51  return;
52  }
53 
54  // Halt the process. Brings it out of the scheduler
55  // and also prevents other processes from waking it up while
56  // it is being restarted by us.
57  result = ProcessCtl(msg->pid, Stop);
58  if (result != API::Success)
59  {
60  ERROR("failed to stop PID " << msg->pid <<
61  ": result = " << (int) result);
63  return;
64  }
65 
66  // Retrieve full command string
67  char cmd[128];
68  const Arch::MemoryMap map;
69  const Memory::Range argRange = map.range(MemoryMap::UserArgs);
70 
71  result = VMCopy(msg->pid, API::Read, (Address) cmd, argRange.virt, sizeof(cmd));
72  if (result != API::Success)
73  {
74  ERROR("failed to read command string from PID " << msg->pid <<
75  ": result = " << (int) result);
77  return;
78  }
79  cmd[sizeof(cmd) - 1] = 0;
80 
81  // Fetch program path from command string
82  List<String> progpath = String(cmd).split(' ');
83  if (progpath.count() == 0)
84  {
85  ERROR("failed to find program path for PID " << msg->pid);
87  return;
88  }
89 
90  // Write fresh copy of the program inside
91  if (!reloadProgram(msg->pid, *progpath[0]))
92  {
93  ERROR("failed to reload PID " << msg->pid <<
94  " from " << *progpath[0] <<
95  ": result = " << (int) result);
97  return;
98  }
99 
100  // Continue program
101  result = ProcessCtl(msg->pid, Resume);
102  if (result != API::Success)
103  {
104  ERROR("failed to resume PID " << msg->pid <<
105  ": result = " << (int) result);
106  msg->result = Recovery::IOError;
107  return;
108  }
109 
110  // Success
111  msg->result = Recovery::Success;
112 }
113 
115  const char *path) const
116 {
117  const FileSystemClient fs;
119  Size fd;
120 
121  DEBUG("pid = " << pid << " path = " << path);
122 
123  // Retrieve file information
124  const FileSystem::Result statResult = fs.statFile(path, &st);
125  if (statResult != FileSystem::Success)
126  {
127  ERROR("failed to statFile() for " << path << ": result = " << (int) statResult);
128  return false;
129  }
130 
131  // Map memory buffer for the compressed program image
132  Memory::Range compressed;
133  compressed.virt = ZERO;
134  compressed.phys = ZERO;
135  compressed.size = st.size;
137 
138  // Create memory mapping
139  API::Result mapResult = VMCtl(SELF, MapContiguous, &compressed);
140  if (mapResult != API::Success)
141  {
142  ERROR("failed to map compressed memory: result = " << (int) mapResult);
143  return false;
144  }
145 
146  // Open file
147  const FileSystem::Result openResult = fs.openFile(path, fd);
148  if (openResult != FileSystem::Success)
149  {
150  ERROR("failed to openFile() for " << path <<
151  ": result = " << (int) openResult);
152  VMCtl(SELF, Release, &compressed);
153  return false;
154  }
155 
156  // Read the program image
157  Size num = st.size;
158  const FileSystem::Result readResult = fs.readFile(fd, (void *) compressed.virt, &num);
159  if (readResult != FileSystem::Success || num != st.size)
160  {
161  ERROR("failed to readFile() for " << path <<
162  ": result = " << (int) readResult << ", num = " << num);
163  VMCtl(SELF, Release, &compressed);
164  return false;
165  }
166 
167  // Close file
168  const FileSystem::Result closeResult = fs.closeFile(fd);
169  if (closeResult != FileSystem::Success)
170  {
171  ERROR("failed to closeFile() for " << path <<
172  ": result = " << (int) closeResult);
173  VMCtl(SELF, Release, &compressed);
174  return false;
175  }
176 
177  // Initialize decompressor
178  Lz4Decompressor lz4((const void *) compressed.virt, st.size);
179  Lz4Decompressor::Result lz4Result = lz4.initialize();
180  if (lz4Result != Lz4Decompressor::Success)
181  {
182  ERROR("failed to initialize LZ4 decompressor: result = " << (int) lz4Result);
183  VMCtl(SELF, Release, &compressed);
184  return false;
185  }
186 
187  // Map memory buffer for the uncompressed program image
188  Memory::Range uncompressed;
189  uncompressed.virt = ZERO;
190  uncompressed.phys = ZERO;
191  uncompressed.size = lz4.getUncompressedSize();
193 
194  // Create memory mapping
195  mapResult = VMCtl(SELF, MapContiguous, &uncompressed);
196  if (mapResult != API::Success)
197  {
198  ERROR("failed to map uncompressed memory: result = " << (int) mapResult);
199  VMCtl(SELF, Release, &compressed);
200  return false;
201  }
202 
203  // Decompress entire file
204  lz4Result = lz4.read((void *)uncompressed.virt, lz4.getUncompressedSize());
205  if (lz4Result != Lz4Decompressor::Success)
206  {
207  ERROR("failed to decompress file: result = " << (int) lz4Result);
208  VMCtl(SELF, Release, &compressed);
209  VMCtl(SELF, Release, &uncompressed);
210  return false;
211  }
212 
213  // Release current memory pages
214  if (!cleanupProgram(pid))
215  {
216  ERROR("failed to cleanup program data for PID " << pid);
217  VMCtl(SELF, Release, &compressed);
218  VMCtl(SELF, Release, &uncompressed);
219  return false;
220  }
221 
222  // Write to program
223  if (!rewriteProgram(pid, uncompressed.virt, num))
224  {
225  ERROR("failed to reset data for PID " << pid);
226  VMCtl(SELF, Release, &compressed);
227  VMCtl(SELF, Release, &uncompressed);
228  return false;
229  }
230 
231  // Cleanup program buffers
232  API::Result releaseResult = VMCtl(SELF, Release, &compressed);
233  if (releaseResult != API::Success)
234  {
235  DEBUG("failed to release compressed memory: result = " << (int) releaseResult);
236  VMCtl(SELF, Release, &uncompressed);
237  return false;
238  }
239 
240  releaseResult = VMCtl(SELF, Release, &uncompressed);
241  if (releaseResult != API::Success)
242  {
243  DEBUG("failed to release uncompressed memory: result = " << (int) releaseResult);
244  return false;
245  }
246 
247  // Success
248  return true;
249 }
250 
252 {
253  const Arch::MemoryMap map;
254  Memory::Range range;
255 
256  DEBUG("pid = " << pid);
257 
258  range = map.range(MemoryMap::UserData);
259  const API::Result dataResult = VMCtl(pid, ReleaseSections, &range);
260  if (dataResult != API::Success)
261  {
262  ERROR("failed to release UserData region in PID " << pid <<
263  ": result = " << (int) dataResult);
264  return false;
265  }
266 
267  range = map.range(MemoryMap::UserHeap);
268  const API::Result heapResult = VMCtl(pid, ReleaseSections, &range);
269  if (heapResult != API::Success)
270  {
271  ERROR("failed to release UserHeap region in PID " << pid <<
272  ": result = " << (int) heapResult);
273  return false;
274  }
275 
276  range = map.range(MemoryMap::UserPrivate);
277  const API::Result privResult = VMCtl(pid, ReleaseSections, &range);
278  if (privResult != API::Success)
279  {
280  ERROR("failed to release UserPrivate region in PID " << pid <<
281  ": result = " << (int) privResult);
282  return false;
283  }
284 
285  return true;
286 }
287 
289  const Address program,
290  const Size size) const
291 {
292  ExecutableFormat *fmt;
293  ExecutableFormat::Region regions[16];
294  Arch::MemoryMap map;
295  Memory::Range range;
296  Size numRegions = 16;
297  Address entry = 0;
298 
299  DEBUG("pid = " << pid << " program = " << (void *) program << " size = " << size);
300 
301  // Attempt to read executable format
302  const ExecutableFormat::Result execResult =
303  ExecutableFormat::find((u8 *) program, size, &fmt);
304 
305  if (execResult != ExecutableFormat::Success)
306  {
307  ERROR("failed to parse executable: result = " << (int) execResult);
308  return false;
309  }
310 
311  // Find entry point
312  const ExecutableFormat::Result entryResult = fmt->entry(&entry);
313  if (entryResult != ExecutableFormat::Success)
314  {
315  ERROR("failed to retrieve entry point: result = " << (int) entryResult);
316  delete fmt;
317  return false;
318  }
319 
320  // Retrieve memory regions
321  const ExecutableFormat::Result regionResult = fmt->regions(regions, &numRegions);
322  if (regionResult != ExecutableFormat::Success)
323  {
324  ERROR("failed to retrieve regions: result = " << (int) regionResult);
325  delete fmt;
326  return false;
327  }
328 
329  // Release buffers
330  delete fmt;
331 
332  // Map program regions into virtual memory of the new process
333  for (Size i = 0; i < numRegions; i++)
334  {
335  // Setup memory range to copy region data
336  range.virt = regions[i].virt;
337  range.phys = ZERO;
338  range.size = regions[i].memorySize;
339  range.access = regions[i].access;
340 
341  // Create mapping in the process
342  const API::Result mapResult = VMCtl(pid, MapContiguous, &range);
343  if (mapResult != API::Success)
344  {
345  ERROR("failed to map " << (void *) regions[i].virt <<
346  " in PID " << pid << ": result = " << (int) mapResult);
347  return false;
348  }
349 
350  // Map inside our process
351  range.virt = ZERO;
352  const API::Result selfResult = VMCtl(SELF, MapContiguous, &range);
353  if (selfResult != API::Success)
354  {
355  ERROR("failed to map " << (void *) regions[i].virt <<
356  ": result = " << (int) selfResult);
357  return false;
358  }
359 
360  // Copy data bytes
361  MemoryBlock::copy((void *)range.virt, (const void *)(program + regions[i].dataOffset),
362  regions[i].dataSize);
363 
364  // Nulify remaining space
365  if (regions[i].memorySize > regions[i].dataSize)
366  {
367  MemoryBlock::set((void *)(range.virt + regions[i].dataSize), 0,
368  regions[i].memorySize - regions[i].dataSize);
369  }
370 
371  // Remove temporary mapping
372  const API::Result unmapResult = VMCtl(SELF, UnMap, &range);
373  if (unmapResult != API::Success)
374  {
375  ERROR("failed to unmap " << (void *) regions[i].virt <<
376  ": result = " << (int) unmapResult);
377  return false;
378  }
379  }
380 
381  // Reset program registers
382  const API::Result resetResult = ProcessCtl(pid, Reset, entry);
383  if (resetResult != API::Success)
384  {
385  ERROR("failed to reset PID " << pid <<
386  ": result = " << (int) resetResult);
387  return false;
388  }
389 
390  // Success
391  return true;
392 }
ExecutableFormat.h
RecoveryServer
Recovery Server.
Definition: RecoveryServer.h:53
RecoveryServer::reloadProgram
bool reloadProgram(const ProcessID pid, const char *path) const
Overwrite the given process by fetching a fresh program data copy.
Definition: RecoveryServer.cpp:114
MemoryBlock::copy
static Size copy(void *dest, const void *src, Size count)
Copy memory from one place to another.
Definition: MemoryBlock.cpp:36
Memory::Range
Memory range.
Definition: Memory.h:55
ProcessInfo
Process information structure, used for Info.
Definition: ProcessCtl.h:63
Recovery::InvalidArgument
@ InvalidArgument
Definition: Recovery.h:46
API::Result
Result
Enumeration of generic kernel API result codes.
Definition: API.h:68
MemoryBlock::set
static void * set(void *dest, int ch, unsigned count)
Fill memory with a constant byte.
Definition: MemoryBlock.cpp:25
Recovery::Success
@ Success
Definition: Recovery.h:44
ExecutableFormat::Region::access
Memory::Access access
Definition: ExecutableFormat.h:61
ExecutableFormat::Region
Memory region.
Definition: ExecutableFormat.h:55
Memory::Writable
@ Writable
Definition: Memory.h:42
FileSystemClient::statFile
FileSystem::Result statFile(const char *path, FileSystem::FileStat *st) const
Retrieve status of a file.
Definition: FileSystemClient.cpp:186
RecoveryServer::restartProcess
void restartProcess(RecoveryMessage *msg)
Restart a process.
Definition: RecoveryServer.cpp:31
String
Abstraction of strings.
Definition: String.h:41
Memory::User
@ User
Definition: Memory.h:44
Lz4Decompressor::getUncompressedSize
u64 getUncompressedSize() const
Get size of the uncompressed data.
Definition: Lz4Decompressor.cpp:125
FileSystemClient
FileSystemClient provides a simple interface to a FileSystemServer.
Definition: FileSystemClient.h:42
Resume
@ Resume
Definition: ProcessCtl.h:55
Lz4Decompressor::initialize
Result initialize()
Initialize the decompressor.
Definition: Lz4Decompressor.cpp:35
ProcessID
u32 ProcessID
Process Identification Number.
Definition: Types.h:140
Address
unsigned long Address
A memory address.
Definition: Types.h:131
ExecutableFormat::regions
virtual Result regions(Region *regions, Size *count) const =0
Memory regions a program needs at runtime.
VMCtl
API::Result VMCtl(const ProcessID procID, const MemoryOperation op, Memory::Range *range=ZERO)
Prototype for user applications.
Definition: VMCtl.h:61
UnMap
@ UnMap
Definition: VMCtl.h:39
MemoryMap::UserData
@ UserData
< User program data from libexec, e.g.
Definition: MemoryMap.h:56
FileSystem::Success
@ Success
Definition: FileSystem.h:54
ProcessCtl
API::Result ProcessCtl(const ProcessID proc, const ProcessOperation op, const Address addr=0, const Address output=0)
Prototype for user applications.
Definition: ProcessCtl.h:93
Reset
@ Reset
Definition: ProcessCtl.h:56
RecoveryMessage
Recovery IPC message.
Definition: RecoveryMessage.h:36
Memory::Readable
@ Readable
Definition: Memory.h:41
ExecutableFormat::find
static Result find(const u8 *image, const Size size, ExecutableFormat **fmt)
Find a ExecutableFormat which can handle the given format.
Definition: ExecutableFormat.cpp:32
Log.h
ExecutableFormat::Region::dataOffset
Address dataOffset
Definition: ExecutableFormat.h:58
Lz4Decompressor::Success
@ Success
Definition: Lz4Decompressor.h:72
ExecutableFormat::Region::virt
Address virt
Definition: ExecutableFormat.h:57
API::Read
@ Read
Definition: API.h:98
ExecutableFormat::Result
Result
Result code.
Definition: ExecutableFormat.h:68
String::split
List< String > split(const char delimiter) const
Split the String into parts separated by a delimiter.
Definition: String.cpp:408
ARMMap
Memory mapping for the kernel and user processes on the ARM architecture.
Definition: ARMMap.h:37
SELF
#define SELF
Definition: ProcessID.h:35
Lz4Decompressor.h
Recovery::IOError
@ IOError
Definition: Recovery.h:45
FileSystem::FileStat::size
Size size
< File access permission bits.
Definition: FileSystem.h:119
DEBUG
#define DEBUG(msg)
Output a debug message to standard output.
Definition: Log.h:89
InfoPID
@ InfoPID
Definition: ProcessCtl.h:47
Release
@ Release
Definition: VMCtl.h:40
Memory::Range::phys
Address phys
Physical address.
Definition: Memory.h:58
FileSystemClient::closeFile
FileSystem::Result closeFile(const Size descriptor) const
Close a file.
Definition: FileSystemClient.cpp:218
FileSystemClient::openFile
FileSystem::Result openFile(const char *path, Size &descriptor) const
Open a file.
Definition: FileSystemClient.cpp:198
MAX_PROCS
#define MAX_PROCS
Maximum number of processes.
Definition: ProcessManager.h:39
MemoryMap::range
Memory::Range range(Region region) const
Get memory range for the given region.
Definition: MemoryMap.cpp:36
ExecutableFormat::Region::memorySize
Size memorySize
Definition: ExecutableFormat.h:60
Recovery::RestartProcess
@ RestartProcess
Definition: Recovery.h:36
RecoveryServer::RecoveryServer
RecoveryServer()
Class constructor function.
Definition: RecoveryServer.cpp:25
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
MemoryMap::UserPrivate
@ UserPrivate
< User private dynamic memory mappings
Definition: MemoryMap.h:59
RecoveryMessage::pid
ProcessID pid
Process identifier of target process.
Definition: RecoveryMessage.h:41
Lz4Decompressor::Result
Result
Result codes.
Definition: Lz4Decompressor.h:70
RecoveryServer.h
FileSystemClient::readFile
FileSystem::Result readFile(const Size descriptor, void *buf, Size *size) const
Read a file.
Definition: FileSystemClient.cpp:229
ChannelServer
Template class which serves incoming messages from Channels using MessageHandlers.
Definition: ChannelServer.h:79
RecoveryServer::rewriteProgram
bool rewriteProgram(const ProcessID pid, const Address program, const Size size) const
Overwrite process with given program data.
Definition: RecoveryServer.cpp:288
ExecutableFormat::entry
virtual Result entry(Address *entry) const =0
Lookup the program entry point.
FileSystemClient.h
Lz4Decompressor::read
Result read(void *buffer, const Size size) const
Reads compressed data.
Definition: Lz4Decompressor.cpp:130
FileSystem::FileStat
Contains file information.
Definition: FileSystem.h:113
ERROR
#define ERROR(msg)
Output an error message.
Definition: Log.h:61
entry
u32 entry[]
Definition: IntelACPI.h:64
RecoveryMessage::result
Recovery::Result result
Result of action.
Definition: RecoveryMessage.h:40
FileSystem::Result
Result
Result code for filesystem Actions.
Definition: FileSystem.h:52
u8
unsigned char u8
Unsigned 8-bit number.
Definition: Types.h:59
ReleaseSections
@ ReleaseSections
Definition: VMCtl.h:41
Lz4Decompressor
Decompress data using the LZ4 algorithm created by Yann Collet.
Definition: Lz4Decompressor.h:39
ExecutableFormat::Region::dataSize
Size dataSize
Definition: ExecutableFormat.h:59
API::Success
@ Success
Definition: API.h:70
ChannelServer< RecoveryServer, RecoveryMessage >::addIPCHandler
void addIPCHandler(const Size slot, IPCHandlerFunction h, const bool sendReply=true)
Register a new IPC message action handler.
Definition: ChannelServer.h:203
RecoveryServer::cleanupProgram
bool cleanupProgram(const ProcessID pid) const
Release and unmap program data.
Definition: RecoveryServer.cpp:251
Memory::Range::virt
Address virt
Virtual address.
Definition: Memory.h:57
MemoryMap::UserArgs
@ UserArgs
< Used for copying program arguments and file descriptors
Definition: MemoryMap.h:61
List< String >
MemoryMap::UserHeap
@ UserHeap
< User heap
Definition: MemoryMap.h:57
VMCopy
API::Result VMCopy(const ProcessID proc, const API::Operation how, const Address ours, const Address theirs, const Size sz)
Prototype for user applications.
Definition: VMCopy.h:42
Memory::Range::size
Size size
Size in number of bytes.
Definition: Memory.h:59
Memory::Range::access
Access access
Page access flags.
Definition: Memory.h:60
ZERO
#define ZERO
Zero value.
Definition: Macros.h:43
MapContiguous
@ MapContiguous
Definition: VMCtl.h:37
Recovery::NotFound
@ NotFound
Definition: Recovery.h:47
ExecutableFormat
Abstraction class of various executable formats.
Definition: ExecutableFormat.h:48
ExecutableFormat::Success
@ Success
Definition: ExecutableFormat.h:70
Stop
@ Stop
Definition: ProcessCtl.h:54