libuav original
Dependents: UAVCAN UAVCAN_Subscriber
basic_file_server_backend.hpp
00001 /**************************************************************************** 00002 * 00003 * Copyright (c) 2015 PX4 Development Team. All rights reserved. 00004 * Author: Pavel Kirienko <pavel.kirienko@gmail.com> 00005 * David Sidrane <david_s5@usa.net> 00006 * 00007 ****************************************************************************/ 00008 00009 #ifndef UAVCAN_POSIX_BASIC_FILE_SERVER_BACKEND_HPP_INCLUDED 00010 #define UAVCAN_POSIX_BASIC_FILE_SERVER_BACKEND_HPP_INCLUDED 00011 00012 #include <sys/stat.h> 00013 #include <cstdio> 00014 #include <cstddef> 00015 #include <cstdlib> 00016 #include <cstring> 00017 #include <cerrno> 00018 #include <ctime> 00019 #include <unistd.h> 00020 #include <fcntl.h> 00021 00022 #include <uavcan/node/timer.hpp> 00023 #include <uavcan/data_type.hpp> 00024 #include <uavcan/protocol/file/Error.hpp> 00025 #include <uavcan/protocol/file/EntryType.hpp> 00026 #include <uavcan/protocol/file/Read.hpp> 00027 #include <uavcan/protocol/file_server.hpp> 00028 #include <uavcan/data_type.hpp> 00029 00030 namespace uavcan_posix 00031 { 00032 /** 00033 * This interface implements a POSIX compliant IFileServerBackend interface 00034 */ 00035 class BasicFileServerBackend : public uavcan::IFileServerBackend 00036 { 00037 enum { FilePermissions = 438 }; ///< 0o666 00038 00039 protected: 00040 class FDCacheBase 00041 { 00042 public: 00043 FDCacheBase() { } 00044 virtual ~FDCacheBase() { } 00045 00046 virtual int open(const char* path, int oflags) 00047 { 00048 using namespace std; 00049 00050 return ::open(path, oflags); 00051 } 00052 00053 virtual int close(int fd, bool done = true) 00054 { 00055 (void)done; 00056 using namespace std; 00057 00058 return ::close(fd); 00059 } 00060 00061 virtual void init() { } 00062 }; 00063 00064 FDCacheBase fallback_; 00065 00066 class FDCache : public FDCacheBase, protected uavcan::TimerBase 00067 { 00068 /// Age in Seconds an entry will stay in the cache if not accessed. 00069 enum { MaxAgeSeconds = 7 }; 00070 00071 /// Rate in Seconds that the cache will be flushed of stale entries. 00072 enum { GarbageCollectionSeconds = 60 }; 00073 00074 class FDCacheItem : uavcan::Noncopyable 00075 { 00076 friend FDCache; 00077 00078 FDCacheItem* next_; 00079 std::time_t last_access_; 00080 const int fd_; 00081 const int oflags_; 00082 const char* const path_; 00083 00084 public: 00085 enum { InvalidFD = -1 }; 00086 00087 FDCacheItem() : 00088 next_(UAVCAN_NULLPTR), 00089 last_access_(0), 00090 fd_(InvalidFD), 00091 oflags_(0), 00092 path_(UAVCAN_NULLPTR) 00093 { } 00094 00095 FDCacheItem(int fd, const char* path, int oflags) : 00096 next_(UAVCAN_NULLPTR), 00097 last_access_(0), 00098 fd_(fd), 00099 oflags_(oflags), 00100 path_(::strndup(path, uavcan::protocol::file::Path::FieldTypes::path::MaxSize)) 00101 { } 00102 00103 ~FDCacheItem() 00104 { 00105 using namespace std; 00106 if (valid()) 00107 { 00108 ::free(const_cast<char*>(path_)); 00109 } 00110 } 00111 00112 bool valid() const 00113 { 00114 return path_ != UAVCAN_NULLPTR; 00115 } 00116 00117 int getFD() const 00118 { 00119 return fd_; 00120 } 00121 00122 std::time_t getAccess() const 00123 { 00124 return last_access_; 00125 } 00126 00127 std::time_t acessed() 00128 { 00129 using namespace std; 00130 last_access_ = time(UAVCAN_NULLPTR); 00131 return getAccess(); 00132 } 00133 00134 void expire() 00135 { 00136 last_access_ = 0; 00137 } 00138 00139 bool expired() const 00140 { 00141 using namespace std; 00142 return 0 == last_access_ || (time(UAVCAN_NULLPTR) - last_access_) > MaxAgeSeconds; 00143 } 00144 00145 bool equals(const char* path, int oflags) const 00146 { 00147 using namespace std; 00148 return oflags_ == oflags && 0 == ::strcmp(path, path_); 00149 } 00150 00151 bool equals(int fd) const 00152 { 00153 return fd_ == fd; 00154 } 00155 }; 00156 00157 FDCacheItem* head_; 00158 00159 FDCacheItem* find(const char* path, int oflags) 00160 { 00161 for (FDCacheItem* pi = head_; pi; pi = pi->next_) 00162 { 00163 if (pi->equals(path, oflags)) 00164 { 00165 return pi; 00166 } 00167 } 00168 return UAVCAN_NULLPTR; 00169 } 00170 00171 FDCacheItem* find(int fd) 00172 { 00173 for (FDCacheItem* pi = head_; pi; pi = pi->next_) 00174 { 00175 if (pi->equals(fd)) 00176 { 00177 return pi; 00178 } 00179 } 00180 return UAVCAN_NULLPTR; 00181 } 00182 00183 FDCacheItem* add(FDCacheItem* pi) 00184 { 00185 pi->next_ = head_; 00186 head_ = pi; 00187 pi->acessed(); 00188 return pi; 00189 } 00190 00191 void removeExpired(FDCacheItem** pi) 00192 { 00193 while (*pi) 00194 { 00195 if ((*pi)->expired()) 00196 { 00197 FDCacheItem* next = (*pi)->next_; 00198 (void)FDCacheBase::close((*pi)->fd_); 00199 delete (*pi); 00200 *pi = next; 00201 continue; 00202 } 00203 pi = &(*pi)->next_; 00204 } 00205 } 00206 00207 void remove(FDCacheItem* pi, bool done) 00208 { 00209 if (done) 00210 { 00211 pi->expire(); 00212 } 00213 removeExpired(&head_); 00214 } 00215 00216 void clear() 00217 { 00218 FDCacheItem* tmp; 00219 for (FDCacheItem* pi = head_; pi; pi = tmp) 00220 { 00221 tmp = pi->next_; 00222 (void)FDCacheBase::close(pi->fd_); 00223 delete pi; 00224 } 00225 } 00226 00227 /* Removed stale entries. In the normal case a node will read the 00228 * complete contents of a file and the read of the last block will 00229 * cause the method remove() to be invoked with done true. Thereby 00230 * flushing the entry from the cache. But if the node does not 00231 * stay the course of the read, it may leave a dangling entry. 00232 * This call back handles the garbage collection. 00233 */ 00234 virtual void handleTimerEvent(const uavcan::TimerEvent&) 00235 { 00236 removeExpired(&head_); 00237 } 00238 00239 public: 00240 FDCache(uavcan::INode& node) : 00241 TimerBase(node), 00242 head_(UAVCAN_NULLPTR) 00243 { } 00244 00245 virtual ~FDCache() 00246 { 00247 stop(); 00248 clear(); 00249 } 00250 00251 virtual void init() 00252 { 00253 startPeriodic(uavcan::MonotonicDuration::fromMSec(GarbageCollectionSeconds * 1000)); 00254 } 00255 00256 virtual int open(const char* path, int oflags) 00257 { 00258 int fd = FDCacheItem::InvalidFD; 00259 00260 FDCacheItem* pi = find(path, oflags); 00261 00262 if (pi != UAVCAN_NULLPTR) 00263 { 00264 pi->acessed(); 00265 } 00266 else 00267 { 00268 fd = FDCacheBase::open(path, oflags); 00269 if (fd < 0) 00270 { 00271 return fd; 00272 } 00273 00274 /* Allocate and clone path */ 00275 00276 pi = new FDCacheItem(fd, path, oflags); 00277 00278 /* Allocation worked but check clone */ 00279 00280 if (pi && !pi->valid()) 00281 { 00282 /* Allocation worked but clone or path failed */ 00283 delete pi; 00284 pi = UAVCAN_NULLPTR; 00285 } 00286 00287 if (pi == UAVCAN_NULLPTR) 00288 { 00289 /* 00290 * If allocation fails no harm just can not cache it 00291 * return open fd 00292 */ 00293 return fd; 00294 } 00295 /* add new */ 00296 add(pi); 00297 } 00298 return pi->getFD(); 00299 } 00300 00301 virtual int close(int fd, bool done) 00302 { 00303 FDCacheItem* pi = find(fd); 00304 if (pi == UAVCAN_NULLPTR) 00305 { 00306 /* 00307 * If not found just close it 00308 */ 00309 return FDCacheBase::close(fd); 00310 } 00311 remove(pi, done); 00312 return 0; 00313 } 00314 }; 00315 00316 FDCacheBase* fdcache_; 00317 uavcan::INode& node_; 00318 00319 FDCacheBase& getFDCache() 00320 { 00321 if (fdcache_ == UAVCAN_NULLPTR) 00322 { 00323 fdcache_ = new FDCache(node_); 00324 00325 if (fdcache_ == UAVCAN_NULLPTR) 00326 { 00327 fdcache_ = &fallback_; 00328 } 00329 00330 fdcache_->init(); 00331 } 00332 return *fdcache_; 00333 } 00334 00335 /** 00336 * Back-end for uavcan.protocol.file.GetInfo. 00337 * Implementation of this method is required. 00338 * On success the method must return zero. 00339 */ 00340 virtual uavcan::int16_t getInfo(const Path& path, uavcan::uint64_t& out_size, EntryType& out_type) 00341 { 00342 int rv = uavcan::protocol::file::Error::INVALID_VALUE; 00343 00344 if (path.size() > 0) 00345 { 00346 using namespace std; 00347 00348 struct stat sb; 00349 00350 rv = stat(path.c_str(), &sb); 00351 00352 if (rv < 0) 00353 { 00354 rv = errno; 00355 } 00356 else 00357 { 00358 rv = 0; 00359 out_size = sb.st_size; 00360 out_type.flags = uavcan::protocol::file::EntryType::FLAG_READABLE; 00361 if (S_ISDIR(sb.st_mode)) 00362 { 00363 out_type.flags |= uavcan::protocol::file::EntryType::FLAG_DIRECTORY; 00364 } 00365 else if (S_ISREG(sb.st_mode)) 00366 { 00367 out_type.flags |= uavcan::protocol::file::EntryType::FLAG_FILE; 00368 } 00369 // TODO Using fixed flag FLAG_READABLE until we add file permission checks to return actual value. 00370 } 00371 } 00372 return rv; 00373 } 00374 00375 /** 00376 * Back-end for uavcan.protocol.file.Read. 00377 * Implementation of this method is required. 00378 * @ref inout_size is set to @ref ReadSize; read operation is required to return exactly this amount, except 00379 * if the end of file is reached. 00380 * On success the method must return zero. 00381 */ 00382 virtual uavcan::int16_t read(const Path& path, const uavcan::uint64_t offset, uavcan::uint8_t* out_buffer, 00383 uavcan::uint16_t& inout_size) 00384 { 00385 int rv = uavcan::protocol::file::Error::INVALID_VALUE; 00386 00387 if (path.size() > 0 && inout_size != 0) 00388 { 00389 using namespace std; 00390 00391 FDCacheBase& cache = getFDCache(); 00392 int fd = cache.open(path.c_str(), O_RDONLY); 00393 00394 if (fd < 0) 00395 { 00396 rv = errno; 00397 } 00398 else 00399 { 00400 ssize_t total_read = 0; 00401 00402 rv = ::lseek(fd, offset, SEEK_SET); 00403 00404 if (rv < 0) 00405 { 00406 rv = errno; 00407 } 00408 else 00409 { 00410 rv = 0; 00411 ssize_t remaining = inout_size; 00412 ssize_t nread = 0; 00413 do 00414 { 00415 nread = ::read(fd, &out_buffer[total_read], remaining); 00416 if (nread < 0) 00417 { 00418 rv = errno; 00419 } 00420 else 00421 { 00422 remaining -= nread, 00423 total_read += nread; 00424 } 00425 } 00426 while (nread > 0 && remaining > 0); 00427 } 00428 00429 (void)cache.close(fd, rv != 0 || total_read != inout_size); 00430 inout_size = total_read; 00431 } 00432 } 00433 return rv; 00434 } 00435 00436 public: 00437 BasicFileServerBackend(uavcan::INode& node) : 00438 fdcache_(UAVCAN_NULLPTR), 00439 node_(node) 00440 { } 00441 00442 ~BasicFileServerBackend() 00443 { 00444 if (fdcache_ != &fallback_) 00445 { 00446 delete fdcache_; 00447 fdcache_ = UAVCAN_NULLPTR; 00448 } 00449 } 00450 }; 00451 00452 #if __GNUC__ 00453 /// Typo fix in a backwards-compatible way (only for GCC projects). Will be removed someday. 00454 typedef BasicFileServerBackend 00455 BasicFileSeverBackend // Missing 'r' 00456 __attribute__((deprecated)); 00457 #endif 00458 00459 } 00460 00461 #endif // Include guard
Generated on Tue Jul 12 2022 17:17:30 by 1.7.2