changeset 3181:6fd38327e777

Fix issue #130 (Orthanc failed to start when /tmp partition was full)
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 31 Jan 2019 15:33:27 +0100
parents 07a2f637b76d
children 5d51f87d8326
files Core/Compression/ZipWriter.cpp Core/TemporaryFile.cpp Core/TemporaryFile.h NEWS OrthancServer/OrthancConfiguration.cpp OrthancServer/OrthancConfiguration.h OrthancServer/OrthancRestApi/OrthancRestArchive.cpp OrthancServer/ServerJobs/ArchiveJob.cpp OrthancServer/ServerJobs/Operations/SystemCallOperation.cpp Resources/Configuration.json UnitTestsSources/ImageTests.cpp
diffstat 11 files changed, 115 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Compression/ZipWriter.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/Core/Compression/ZipWriter.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -148,6 +148,7 @@
 
     if (!pimpl_->file_)
     {
+      LOG(ERROR) << "Cannot create new ZIP archive: " << path_;
       throw OrthancException(ErrorCode_CannotWriteFile);
     }
   }
@@ -169,7 +170,8 @@
     if (level >= 10)
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange,
-                             "ZIP compression level must be between 0 (no compression) and 9 (highest compression)");
+                             "ZIP compression level must be between 0 (no compression) "
+                             "and 9 (highest compression)");
     }
 
     Close();
@@ -208,6 +210,7 @@
 
     if (result != 0)
     {
+      LOG(ERROR) << "Cannot add new file inside ZIP archive: " << path;
       throw OrthancException(ErrorCode_CannotWriteFile);
     }
 
@@ -239,6 +242,7 @@
 
       if (zipWriteInFileInZip(pimpl_->file_, data, bytes))
       {
+        LOG(ERROR) << "Cannot write data to ZIP archive: " << path_;
         throw OrthancException(ErrorCode_CannotWriteFile);
       }
       
@@ -253,6 +257,4 @@
     Close();
     append_ = append;
   }
-    
-
 }
--- a/Core/TemporaryFile.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/Core/TemporaryFile.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -34,6 +34,7 @@
 #include "PrecompiledHeaders.h"
 #include "TemporaryFile.h"
 
+#include "OrthancException.h"
 #include "SystemToolbox.h"
 #include "Toolbox.h"
 
@@ -41,15 +42,25 @@
 
 namespace Orthanc
 {
-  static std::string CreateTemporaryPath(const char* extension)
+  static std::string CreateTemporaryPath(const char* temporaryDirectory,
+                                         const char* extension)
   {
+    boost::filesystem::path dir;
+
+    if (temporaryDirectory == NULL)
+    {
 #if BOOST_HAS_FILESYSTEM_V3 == 1
-    boost::filesystem::path tmpDir = boost::filesystem::temp_directory_path();
+      dir = boost::filesystem::temp_directory_path();
 #elif defined(__linux__)
-    boost::filesystem::path tmpDir("/tmp");
+      dir = "/tmp";
 #else
-#error Support your platform here
+#  error Support your platform here
 #endif
+    }
+    else
+    {
+      dir = temporaryDirectory;
+    }
 
     // We use UUID to create unique path to temporary files
     std::string filename = "Orthanc-" + Orthanc::Toolbox::GenerateUuid();
@@ -59,19 +70,20 @@
       filename.append(extension);
     }
 
-    tmpDir /= filename;
-    return tmpDir.string();
+    dir /= filename;
+    return dir.string();
   }
 
 
   TemporaryFile::TemporaryFile() : 
-    path_(CreateTemporaryPath(NULL))
+    path_(CreateTemporaryPath(NULL, NULL))
   {
   }
 
 
-  TemporaryFile::TemporaryFile(const char* extension) :
-    path_(CreateTemporaryPath(extension))
+  TemporaryFile::TemporaryFile(const std::string& temporaryDirectory,
+                               const std::string& extension) :
+    path_(CreateTemporaryPath(temporaryDirectory.c_str(), extension.c_str()))
   {
   }
 
@@ -84,12 +96,39 @@
 
   void TemporaryFile::Write(const std::string& content)
   {
-    SystemToolbox::WriteFile(content, path_);
+    try
+    {
+      SystemToolbox::WriteFile(content, path_);
+    }
+    catch (OrthancException&)
+    {
+      LOG(ERROR) << "Can't create temporary file \"" << path_
+                 << "\" with " << content.size()
+                 << " bytes: Check you have write access to the "
+                 << "temporary directory and that it is not full";
+      throw;
+    }
   }
 
 
   void TemporaryFile::Read(std::string& content) const
   {
-    SystemToolbox::ReadFile(content, path_);
+    try
+    {
+      SystemToolbox::ReadFile(content, path_);
+    }
+    catch (OrthancException&)
+    {
+      LOG(ERROR) << "Can't read temporary file \"" << path_
+                 << "\": Another process has corrupted the temporary directory";
+      throw;
+    }
+  }
+
+
+  void TemporaryFile::Touch()
+  {
+    std::string empty;
+    Write(empty);
   }
 }
--- a/Core/TemporaryFile.h	Wed Jan 30 17:50:51 2019 +0100
+++ b/Core/TemporaryFile.h	Thu Jan 31 15:33:27 2019 +0100
@@ -53,7 +53,8 @@
   public:
     TemporaryFile();
 
-    TemporaryFile(const char* extension);
+    TemporaryFile(const std::string& temporaryFolder,
+                  const std::string& extension);
 
     ~TemporaryFile();
 
@@ -65,5 +66,7 @@
     void Write(const std::string& content);
 
     void Read(std::string& content) const;
+
+    void Touch();
   };
 }
--- a/NEWS	Wed Jan 30 17:50:51 2019 +0100
+++ b/NEWS	Thu Jan 31 15:33:27 2019 +0100
@@ -5,8 +5,9 @@
 -------
 
 * New configuration options:
-  - "MetricsEnabled" to track the metrics of Orthanc
+  - "MetricsEnabled" to enable the tracking of the metrics of Orthanc
   - "HttpThreadsCount" to set the number of threads in the embedded HTTP server
+  - "TemporaryDirectory" to set the folder containing the temporary files
 
 REST API
 --------
@@ -24,6 +25,7 @@
 -----------
 
 * Fix build with unpatched versions of Civetweb (missing "mg_disable_keep_alive()")
+* Fix issue #130 (Orthanc failed to start when /tmp partition was full)
 
 
 Version 1.5.3 (2019-01-25)
--- a/OrthancServer/OrthancConfiguration.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/OrthancServer/OrthancConfiguration.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -38,6 +38,7 @@
 #include "../Core/Logging.h"
 #include "../Core/OrthancException.h"
 #include "../Core/SystemToolbox.h"
+#include "../Core/TemporaryFile.h"
 #include "../Core/Toolbox.h"
 
 #include "ServerIndex.h"
@@ -47,6 +48,7 @@
 static const char* const DICOM_MODALITIES_IN_DB = "DicomModalitiesInDatabase";
 static const char* const ORTHANC_PEERS = "OrthancPeers";
 static const char* const ORTHANC_PEERS_IN_DB = "OrthancPeersInDatabase";
+static const char* const TEMPORARY_DIRECTORY = "TemporaryDirectory";
 
 namespace Orthanc
 {
@@ -826,4 +828,17 @@
   {
     serverIndex_ = NULL;
   }
+
+  
+  TemporaryFile* OrthancConfiguration::CreateTemporaryFile() const
+  {
+    if (json_.isMember(TEMPORARY_DIRECTORY))
+    {
+      return new TemporaryFile(GetStringParameter(TEMPORARY_DIRECTORY, ""), "");
+    }
+    else
+    {
+      return new TemporaryFile;
+    }
+  }
 }
--- a/OrthancServer/OrthancConfiguration.h	Wed Jan 30 17:50:51 2019 +0100
+++ b/OrthancServer/OrthancConfiguration.h	Thu Jan 31 15:33:27 2019 +0100
@@ -47,6 +47,7 @@
 {
   class HttpServer;
   class ServerIndex;
+  class TemporaryFile;
   
   class OrthancConfiguration : public boost::noncopyable
   {
@@ -224,5 +225,7 @@
     void SetServerIndex(ServerIndex& index);
 
     void ResetServerIndex();
+
+    TemporaryFile* CreateTemporaryFile() const;
   };
 }
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -37,6 +37,7 @@
 #include "../../Core/HttpServer/FilesystemHttpSender.h"
 #include "../../Core/OrthancException.h"
 #include "../../Core/SerializationToolbox.h"
+#include "../OrthancConfiguration.h"
 #include "../ServerContext.h"
 #include "../ServerJobs/ArchiveJob.h"
 
@@ -136,7 +137,13 @@
 
     if (synchronous)
     {
-      boost::shared_ptr<TemporaryFile> tmp(new TemporaryFile);
+      boost::shared_ptr<TemporaryFile> tmp;
+
+      {
+        OrthancConfiguration::ReaderLock lock;
+        tmp.reset(lock.GetConfiguration().CreateTemporaryFile());
+      }
+
       job->SetSynchronousTarget(tmp);
     
       Json::Value publicContent;
--- a/OrthancServer/ServerJobs/ArchiveJob.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/OrthancServer/ServerJobs/ArchiveJob.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -39,6 +39,7 @@
 #include "../../Core/DicomParsing/DicomDirWriter.h"
 #include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
+#include "../OrthancConfiguration.h"
 #include "../ServerContext.h"
 
 #include <stdio.h>
@@ -867,13 +868,20 @@
     
     if (synchronousTarget_.get() == NULL)
     {
-      asynchronousTarget_.reset(new TemporaryFile);
+      {
+        OrthancConfiguration::ReaderLock lock;
+        asynchronousTarget_.reset(lock.GetConfiguration().CreateTemporaryFile());
+      }
+
       target = asynchronousTarget_.get();
     }
     else
     {
       target = synchronousTarget_.get();
     }
+
+    assert(target != NULL);
+    target->Touch();  // Make sure we can write to the temporary file
     
     if (writer_.get() != NULL)
     {
--- a/OrthancServer/ServerJobs/Operations/SystemCallOperation.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/OrthancServer/ServerJobs/Operations/SystemCallOperation.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -43,6 +43,7 @@
 #include "../../../Core/TemporaryFile.h"
 #include "../../../Core/Toolbox.h"
 #include "../../../Core/SystemToolbox.h"
+#include "../../OrthancConfiguration.h"
 
 namespace Orthanc
 {
@@ -92,7 +93,11 @@
         std::string dicom;
         instance.ReadDicom(dicom);
 
-        tmp.reset(new TemporaryFile);
+        {
+          OrthancConfiguration::ReaderLock lock;
+          tmp.reset(lock.GetConfiguration().CreateTemporaryFile());
+        }
+
         tmp->Write(dicom);
         
         arguments.push_back(tmp->GetPath());
--- a/Resources/Configuration.json	Wed Jan 30 17:50:51 2019 +0100
+++ b/Resources/Configuration.json	Thu Jan 31 15:33:27 2019 +0100
@@ -17,6 +17,16 @@
   // a RAM-drive or a SSD device for performance reasons.
   "IndexDirectory" : "OrthancStorage",
 
+  // Path to the directory where Orthanc stores its large temporary
+  // files. The corresponding filesystem must be properly sized, given
+  // that for instance a ZIP archive of DICOM images created by a job
+  // can weight several GBs, and that there might be up to
+  // "JobsHistorySize" archives to be stored simultaneously. If not
+  // set, Orthanc will use the default temporary folder of the
+  // operating system (such as "/tmp/" on UNIX-like systems, or
+  // "C:/Temp" on Microsoft Windows).
+  // "TemporaryDirectory" : "/tmp/Orthanc/",
+
   // Enable the transparent compression of the DICOM instances
   "StorageCompression" : false,
 
--- a/UnitTestsSources/ImageTests.cpp	Wed Jan 30 17:50:51 2019 +0100
+++ b/UnitTestsSources/ImageTests.cpp	Thu Jan 31 15:33:27 2019 +0100
@@ -185,7 +185,7 @@
 
   {
     Orthanc::TemporaryFile tmp;
-    Orthanc::SystemToolbox::WriteFile(s, tmp.GetPath());
+    tmp.Write(s);
 
     Orthanc::PngReader r2;
     r2.ReadFromFile(tmp.GetPath());
@@ -411,7 +411,7 @@
 
   {
     Orthanc::TemporaryFile tmp;
-    Orthanc::SystemToolbox::WriteFile(s, tmp.GetPath());
+    tmp.Write(s);
 
     Orthanc::PamReader r2;
     r2.ReadFromFile(tmp.GetPath());
@@ -433,4 +433,3 @@
     }
   }
 }
-