libuav original

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers basic_file_server_backend.hpp Source File

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