F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
FileManager.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title FileManager.hpp
3 // \author bocchino
4 // \brief hpp file for FileManager component implementation class
5 //
6 // \copyright
7 // Copyright 2009-2015, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12 
13 #include <cstdio>
14 #include <cstdlib>
15 
16 #include <Fw/FPrimeBasicTypes.hpp>
17 #include "Fw/Types/Assert.hpp"
19 #include "Os/Directory.hpp"
21 #include "config/FileManagerConfig.hpp"
22 
23 namespace Svc {
24 
25 // ----------------------------------------------------------------------
26 // Construction, initialization, and destruction
27 // ----------------------------------------------------------------------
28 
29 FileManager ::FileManager(const char* const compName
30  )
31  : FileManagerComponentBase(compName),
32  commandCount(0),
33  errorCount(0),
34  m_listState(IDLE),
35  m_totalEntries(0),
36  m_currentOpCode(0),
37  m_currentCmdSeq(0),
38  m_runQueued(false) {}
39 
41 
42 // ----------------------------------------------------------------------
43 // Command handler implementations
44 // ----------------------------------------------------------------------
45 
46 void FileManager ::CreateDirectory_cmdHandler(const FwOpcodeType opCode,
47  const U32 cmdSeq,
48  const Fw::CmdStringArg& dirName) {
49  Fw::LogStringArg logStringDirName(dirName.toChar());
50  this->log_ACTIVITY_HI_CreateDirectoryStarted(logStringDirName);
51  bool errorIfDirExists = true;
52  const Os::FileSystem::Status status = Os::FileSystem::createDirectory(dirName.toChar(), errorIfDirExists);
53  if (status != Os::FileSystem::OP_OK) {
54  this->log_WARNING_HI_DirectoryCreateError(logStringDirName, status);
55  } else {
56  this->log_ACTIVITY_HI_CreateDirectorySucceeded(logStringDirName);
57  }
58  this->emitTelemetry(status);
59  this->sendCommandResponse(opCode, cmdSeq, status);
60 }
61 
62 void FileManager ::RemoveFile_cmdHandler(const FwOpcodeType opCode,
63  const U32 cmdSeq,
64  const Fw::CmdStringArg& fileName,
65  const bool ignoreErrors) {
66  Fw::LogStringArg logStringFileName(fileName.toChar());
67  this->log_ACTIVITY_HI_RemoveFileStarted(logStringFileName);
68  const Os::FileSystem::Status status = Os::FileSystem::removeFile(fileName.toChar());
69  if (status != Os::FileSystem::OP_OK) {
70  this->log_WARNING_HI_FileRemoveError(logStringFileName, status);
71  if (ignoreErrors == true) {
72  ++this->errorCount;
73  this->tlmWrite_Errors(this->errorCount);
74  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
75  return;
76  }
77  } else {
78  this->log_ACTIVITY_HI_RemoveFileSucceeded(logStringFileName);
79  }
80  this->emitTelemetry(status);
81  this->sendCommandResponse(opCode, cmdSeq, status);
82 }
83 
84 void FileManager ::MoveFile_cmdHandler(const FwOpcodeType opCode,
85  const U32 cmdSeq,
86  const Fw::CmdStringArg& sourceFileName,
87  const Fw::CmdStringArg& destFileName) {
88  Fw::LogStringArg logStringSource(sourceFileName.toChar());
89  Fw::LogStringArg logStringDest(destFileName.toChar());
90  this->log_ACTIVITY_HI_MoveFileStarted(logStringSource, logStringDest);
91  const Os::FileSystem::Status status = Os::FileSystem::moveFile(sourceFileName.toChar(), destFileName.toChar());
92  if (status != Os::FileSystem::OP_OK) {
93  this->log_WARNING_HI_FileMoveError(logStringSource, logStringDest, status);
94  } else {
95  this->log_ACTIVITY_HI_MoveFileSucceeded(logStringSource, logStringDest);
96  }
97  this->emitTelemetry(status);
98  this->sendCommandResponse(opCode, cmdSeq, status);
99 }
100 
101 void FileManager ::RemoveDirectory_cmdHandler(const FwOpcodeType opCode,
102  const U32 cmdSeq,
103  const Fw::CmdStringArg& dirName) {
104  Fw::LogStringArg logStringDirName(dirName.toChar());
105  this->log_ACTIVITY_HI_RemoveDirectoryStarted(logStringDirName);
107  if (status != Os::FileSystem::OP_OK) {
108  this->log_WARNING_HI_DirectoryRemoveError(logStringDirName, status);
109  } else {
110  this->log_ACTIVITY_HI_RemoveDirectorySucceeded(logStringDirName);
111  }
112  this->emitTelemetry(status);
113  this->sendCommandResponse(opCode, cmdSeq, status);
114 }
115 
116 void FileManager ::ShellCommand_cmdHandler(const FwOpcodeType opCode,
117  const U32 cmdSeq,
118  const Fw::CmdStringArg& command,
119  const Fw::CmdStringArg& logFileName) {
120  Fw::LogStringArg logStringCommand(command.toChar());
121  this->log_ACTIVITY_HI_ShellCommandStarted(logStringCommand);
122  int status = this->systemCall(command, logFileName);
123  if (status == 0) {
124  this->log_ACTIVITY_HI_ShellCommandSucceeded(logStringCommand);
125  } else {
126  this->log_WARNING_HI_ShellCommandFailed(logStringCommand, static_cast<U32>(status));
127  }
128  this->emitTelemetry(status == 0 ? Os::FileSystem::OP_OK : Os::FileSystem::OTHER_ERROR);
129  this->sendCommandResponse(opCode, cmdSeq, status == 0 ? Os::FileSystem::OP_OK : Os::FileSystem::OTHER_ERROR);
130 }
131 
132 void FileManager ::AppendFile_cmdHandler(const FwOpcodeType opCode,
133  const U32 cmdSeq,
134  const Fw::CmdStringArg& source,
135  const Fw::CmdStringArg& target) {
136  Fw::LogStringArg logStringSource(source.toChar());
137  Fw::LogStringArg logStringTarget(target.toChar());
138  this->log_ACTIVITY_HI_AppendFileStarted(logStringSource, logStringTarget);
139 
140  Os::FileSystem::Status status;
141  status = Os::FileSystem::appendFile(source.toChar(), target.toChar(), true);
142  if (status != Os::FileSystem::OP_OK) {
143  this->log_WARNING_HI_AppendFileFailed(logStringSource, logStringTarget, status);
144  } else {
145  this->log_ACTIVITY_HI_AppendFileSucceeded(logStringSource, logStringTarget);
146  }
147 
148  this->emitTelemetry(status);
149  this->sendCommandResponse(opCode, cmdSeq, status);
150 }
151 
152 void FileManager ::FileSize_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, const Fw::CmdStringArg& fileName) {
153  Fw::LogStringArg logStringFileName(fileName.toChar());
154  this->log_ACTIVITY_HI_FileSizeStarted(logStringFileName);
155 
156  FwSizeType size_arg;
157  const Os::FileSystem::Status status = Os::FileSystem::getFileSize(fileName.toChar(), size_arg);
158  if (status != Os::FileSystem::OP_OK) {
159  this->log_WARNING_HI_FileSizeError(logStringFileName, status);
160  } else {
161  this->log_ACTIVITY_HI_FileSizeSucceeded(logStringFileName, size_arg);
162  }
163  this->emitTelemetry(status);
164  this->sendCommandResponse(opCode, cmdSeq, status);
165 }
166 
167 void FileManager ::ListDirectory_cmdHandler(const FwOpcodeType opCode,
168  const U32 cmdSeq,
169  const Fw::CmdStringArg& dirName) {
170  // Check if we're already listing a directory
171  if (m_listState == LISTING_IN_PROGRESS) {
172  this->log_WARNING_HI_ListDirectoryError(dirName, static_cast<U32>(Os::Directory::OTHER_ERROR));
173  this->sendCommandResponse(opCode, cmdSeq, Os::FileSystem::OTHER_ERROR);
174  return;
175  }
176 
178 
179  // Open the directory for reading
180  Os::Directory::Status status = m_currentDir.open(dirName.toChar(), Os::Directory::OpenMode::READ);
181 
182  if (status != Os::Directory::OP_OK) {
183  this->log_WARNING_HI_ListDirectoryError(dirName, static_cast<U32>(status));
184  this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
185  this->sendCommandResponse(opCode, cmdSeq, Os::FileSystem::OTHER_ERROR);
186  return;
187  }
188 
189  // Initialize state machine for asynchronous processing
190  m_listState = LISTING_IN_PROGRESS;
191  m_currentDirName = dirName;
192  m_currentOpCode = opCode;
193  m_currentCmdSeq = cmdSeq;
194  m_totalEntries = 0;
195 
196  // Directory listing will be processed asynchronously by the rate group.
197  // The schedIn_handler will process FILES_PER_RATE_TICK directory entries per rate tick to
198  // prevent event flooding while maintaining configurable performance.
199  // Command response will be sent when listing completes.
200 }
201 
202 void FileManager ::CalculateCrc_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, const Fw::CmdStringArg& filename) {
203  Os::File file;
204  U32 crcValue = 0;
205  this->log_ACTIVITY_HI_CalculateCrcStarted(filename);
206 
207  Os::File::Status status = file.open(filename.toChar(), Os::File::OPEN_READ);
208  if (status == Os::File::OP_OK) {
209  status = file.calculateCrc(crcValue);
210  }
211 
212  if (status == Os::File::OP_OK) {
213  this->log_ACTIVITY_HI_CalculateCrcSucceeded(filename, crcValue);
214  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK);
215  } else {
216  this->log_WARNING_HI_CalculateCrcFailed(filename, status);
217  this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR);
218  }
219  file.close();
220 }
221 
222 void FileManager ::pingIn_handler(const FwIndexType portNum, U32 key) {
223  // return key
224  this->pingOut_out(0, key);
225 }
226 
227 void FileManager ::schedIn_handler(const FwIndexType portNum, U32 context) {
228  bool isQueued = false;
229  // m_runQueued will be compared to isQueued (false). When equal (i.e. m_runQueued is false) the atomic will be
230  // set to true and the function will return true indicating that a run was successfully marked as queued and thus
231  // the internal handler should be invoked.
232  bool expects_enqueue = this->m_runQueued.compare_exchange_strong(isQueued, true);
233  if (expects_enqueue) {
235  }
236 }
237 
238 void FileManager ::run_internalInterfaceHandler() {
239  FW_ASSERT(this->m_runQueued);
240  this->m_runQueued = false; // Run is not queued anymore (we are running)
241  // Only process if we're in the middle of a directory listing
242  if (m_listState == LISTING_IN_PROGRESS) {
243  // Process multiple files per rate tick based on configuration
244  for (U32 fileCount = 0; fileCount < Svc::FileManagerConfig::FILES_PER_RATE_TICK; fileCount++) {
245  Fw::String filename;
246  Os::Directory::Status status = m_currentDir.read(filename);
247 
248  if (status == Os::Directory::NO_MORE_FILES) {
249  // We're done listing - close directory and send response
250  m_currentDir.close();
251  m_listState = IDLE;
252 
253  this->log_ACTIVITY_HI_ListDirectorySucceeded(m_currentDirName, m_totalEntries);
254  this->emitTelemetry(Os::FileSystem::OP_OK);
255  this->sendCommandResponse(m_currentOpCode, m_currentCmdSeq, Os::FileSystem::OP_OK);
256  break; // Exit the loop since we're done
257 
258  } else if (status == Os::Directory::OP_OK) {
259  // Construct full path for type checking
260  Fw::String fullPath;
261  fullPath.format("%s/%s", m_currentDirName.toChar(), filename.toChar());
262 
263  // Determine entry type
265 
266  if (pathType == Os::FileSystem::FILE) {
267  // Regular file: get size and emit file event
268  FwSizeType fileSize;
269  Os::FileSystem::Status sizeStatus = Os::FileSystem::getFileSize(fullPath.toChar(), fileSize);
271  m_currentDirName, filename,
272  (sizeStatus == Os::FileSystem::OP_OK) ? fileSize : static_cast<FwSizeType>(0));
273  } else if (pathType == Os::FileSystem::DIRECTORY) {
274  // Subdirectory: emit subdirectory event
275  this->log_ACTIVITY_HI_DirectoryListingSubdir(m_currentDirName, filename);
276  } else {
277  // Special file or inaccessible: treat as file with 0 size
278  this->log_ACTIVITY_HI_DirectoryListing(m_currentDirName, filename, static_cast<FwSizeType>(0));
279  }
280 
281  m_totalEntries++;
282 
283  } else {
284  // Error reading directory - close and send error response
285  m_currentDir.close();
286  m_listState = IDLE;
287 
288  this->log_WARNING_HI_ListDirectoryError(m_currentDirName, static_cast<U32>(status));
289  this->emitTelemetry(Os::FileSystem::OTHER_ERROR);
290  this->sendCommandResponse(m_currentOpCode, m_currentCmdSeq, Os::FileSystem::OTHER_ERROR);
291  break; // Exit the loop since we had an error
292  }
293  }
294  }
295 }
296 
297 // ----------------------------------------------------------------------
298 // Helper methods
299 // ----------------------------------------------------------------------
300 
301 int FileManager ::systemCall(const Fw::CmdStringArg& command, const Fw::CmdStringArg& logFileName) const {
302  // Create a buffer of at least enough size for storing the eval string less the 2 %s tokens, two command strings,
303  // and a null terminator at the end
304  const char evalStr[] = "eval '%s' 1>>%s 2>&1\n";
305  constexpr U32 bufferSize = (sizeof(evalStr) - 4) + (2 * FW_CMD_STRING_MAX_SIZE) + 1;
306  char buffer[bufferSize];
307 
308  // Wrap that buffer in an external string for formatting purposes
309  Fw::ExternalString stringBuffer(buffer, bufferSize);
310  Fw::FormatStatus formatStatus = stringBuffer.format(evalStr, command.toChar(), logFileName.toChar());
311  // Since the buffer is exactly sized, the only error can occur is a software error not caused by ground
312  FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS);
313 
314  // Call the system
315  const int status = system(stringBuffer.toChar());
316  return status;
317 }
318 
319 void FileManager ::emitTelemetry(const Os::FileSystem::Status status) {
320  if (status == Os::FileSystem::OP_OK) {
321  ++this->commandCount;
322  this->tlmWrite_CommandsExecuted(this->commandCount);
323  } else {
324  ++this->errorCount;
325  this->tlmWrite_Errors(this->errorCount);
326  }
327 }
328 
329 void FileManager ::sendCommandResponse(const FwOpcodeType opCode,
330  const U32 cmdSeq,
331  const Os::FileSystem::Status status) {
332  this->cmdResponse_out(opCode, cmdSeq,
334 }
335 
336 } // namespace Svc
void log_WARNING_HI_ListDirectoryError(const Fw::StringBase &dirName, U32 status) const
void log_ACTIVITY_HI_DirectoryListingSubdir(const Fw::StringBase &dirName, const Fw::StringBase &subdirName) const
void log_ACTIVITY_HI_CreateDirectoryStarted(const Fw::StringBase &dirName) const
FwIdType FwOpcodeType
The type of a command opcode.
Status calculateCrc(U32 &crc)
calculate the CRC32 of the entire file
Definition: File.cpp:211
void log_ACTIVITY_HI_RemoveDirectorySucceeded(const Fw::StringBase &dirName) const
void log_ACTIVITY_HI_DirectoryListing(const Fw::StringBase &dirName, const Fw::StringBase &fileName, FwSizeType fileSize) const
PlatformSizeType FwSizeType
static Status moveFile(const char *sourcePath, const char *destPath)
Move a file from sourcePath to destPath.
Definition: FileSystem.cpp:207
void log_ACTIVITY_HI_CalculateCrcStarted(const Fw::StringBase &fileName) const
void log_WARNING_HI_FileSizeError(const Fw::StringBase &fileName, U32 status) const
void run_internalInterfaceInvoke()
Internal interface base-class function for run.
FileManager(const char *const compName)
Definition: FileManager.cpp:29
const char * toChar() const
Convert to a C-style char*.
Definition: String.hpp:50
static Status removeDirectory(const char *path)
Remove a directory at the specified path.
Definition: FileSystem.cpp:82
void log_ACTIVITY_HI_FileSizeSucceeded(const Fw::StringBase &fileName, FwSizeType size) const
static Status appendFile(const char *sourcePath, const char *destPath, bool createMissingDest=false)
Append the source file to the destination file.
Definition: FileSystem.cpp:176
void log_ACTIVITY_HI_ListDirectorySucceeded(const Fw::StringBase &dirName, U32 fileCount) const
Auto-generated base for FileManager component.
void log_ACTIVITY_HI_CalculateCrcSucceeded(const Fw::StringBase &fileName, U32 crc) const
void log_ACTIVITY_HI_ListDirectoryStarted(const Fw::StringBase &dirName) const
void pingOut_out(FwIndexType portNum, U32 key)
Invoke output port pingOut.
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
void log_ACTIVITY_HI_MoveFileStarted(const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName) const
void log_ACTIVITY_HI_AppendFileSucceeded(const Fw::StringBase &source, const Fw::StringBase &target) const
void log_ACTIVITY_HI_CreateDirectorySucceeded(const Fw::StringBase &dirName) const
A catch-all for other errors. Have to look in implementation-specific code.
Definition: Directory.hpp:30
void log_ACTIVITY_HI_RemoveDirectoryStarted(const Fw::StringBase &dirName) const
void log_ACTIVITY_HI_ShellCommandSucceeded(const Fw::StringBase &command) const
void close() override
Close directory.
Definition: Directory.cpp:73
Status read(char *fileNameBuffer, FwSizeType buffSize) override
Get next filename from directory stream.
Definition: Directory.cpp:54
A string backed by an external buffer.
void close() override
close the file, if not opened then do nothing
Definition: File.cpp:70
void cmdResponse_out(FwOpcodeType opCode, U32 cmdSeq, Fw::CmdResponse response)
Emit command response.
Directory stream has no more files.
Definition: Directory.hpp:25
void log_ACTIVITY_HI_FileSizeStarted(const Fw::StringBase &fileName) const
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:39
Command successfully executed.
void log_ACTIVITY_HI_MoveFileSucceeded(const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName) const
static Status getFileSize(const char *path, FwSizeType &size)
Get the size of the file (in bytes) at the specified path.
Definition: FileSystem.cpp:225
static Status createDirectory(const char *path, bool errorIfAlreadyExists=false)
Create a new directory at the specified path.
Definition: FileSystem.cpp:110
void log_WARNING_HI_DirectoryRemoveError(const Fw::StringBase &dirName, U32 status) const
Command had execution error.
static PathType getPathType(const char *path)
Return the type of the path (file, directory, or doesn&#39;t exist)
Definition: FileSystem.cpp:137
other OS-specific error
Definition: FileSystem.hpp:40
const char * toChar() const
Convert to a C-style char*.
Definition: CmdString.hpp:50
void log_ACTIVITY_HI_AppendFileStarted(const Fw::StringBase &source, const Fw::StringBase &target) const
Operation was successful.
Definition: File.hpp:40
void tlmWrite_Errors(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
void log_ACTIVITY_HI_RemoveFileStarted(const Fw::StringBase &fileName) const
void log_ACTIVITY_HI_ShellCommandStarted(const Fw::StringBase &command) const
PlatformIndexType FwIndexType
Operation was successful.
Definition: Directory.hpp:20
Open file for reading.
Definition: File.hpp:31
void log_WARNING_HI_AppendFileFailed(const Fw::StringBase &source, const Fw::StringBase &target, U32 status) const
Status open(const char *path, OpenMode mode) override
Open or create a directory.
Definition: Directory.cpp:31
RateGroupDivider component implementation.
void log_WARNING_HI_CalculateCrcFailed(const Fw::StringBase &fileName, U32 status) const
Operation was successful.
Definition: FileSystem.hpp:24
static constexpr U32 FILES_PER_RATE_TICK
void log_WARNING_HI_ShellCommandFailed(const Fw::StringBase &command, U32 status) const
void log_WARNING_HI_DirectoryCreateError(const Fw::StringBase &dirName, U32 status) const
void log_ACTIVITY_HI_RemoveFileSucceeded(const Fw::StringBase &fileName) const
void log_WARNING_HI_FileMoveError(const Fw::StringBase &sourceFileName, const Fw::StringBase &destFileName, U32 status) const
void tlmWrite_CommandsExecuted(U32 arg, Fw::Time _tlmTime=Fw::Time()) const
#define FW_ASSERT(...)
Definition: Assert.hpp:14
static Status removeFile(const char *path)
Remove a file at the specified path.
Definition: FileSystem.cpp:86
void log_WARNING_HI_FileRemoveError(const Fw::StringBase &fileName, U32 status) const
FormatStatus
status of string format calls
Definition: format.hpp:18