changeset 1065:921532f67770

Lua scripts can invoke system commands, with CallSystem()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 25 Jul 2014 16:59:33 +0200
parents cd20e2568fc2
children bb82e5e818e9
files CMakeLists.txt Core/Uuid.h NEWS OrthancServer/Scheduler/CallSystemCommand.cpp OrthancServer/Scheduler/CallSystemCommand.h OrthancServer/ServerContext.cpp Resources/Samples/Lua/CallDcm2Xml.lua Resources/Toolbox.lua
diffstat 8 files changed, 210 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Fri Jul 25 16:13:36 2014 +0200
+++ b/CMakeLists.txt	Fri Jul 25 16:59:33 2014 +0200
@@ -173,6 +173,7 @@
   OrthancServer/Scheduler/ServerScheduler.cpp
   OrthancServer/Scheduler/StorePeerCommand.cpp
   OrthancServer/Scheduler/StoreScuCommand.cpp
+  OrthancServer/Scheduler/CallSystemCommand.cpp
   )
 
 
--- a/Core/Uuid.h	Fri Jul 25 16:13:36 2014 +0200
+++ b/Core/Uuid.h	Fri Jul 25 16:59:33 2014 +0200
@@ -43,6 +43,8 @@
  * http://stackoverflow.com/questions/246930/is-there-any-difference-between-a-guid-and-a-uuid
  **/
 
+#include "Toolbox.h"
+
 namespace Orthanc
 {
   namespace Toolbox
@@ -69,6 +71,16 @@
       {
         return path_;
       }
+
+      void Write(const std::string& content)
+      {
+        Toolbox::WriteFile(content, path_);
+      }
+
+      void Read(std::string& content) const
+      {
+        Toolbox::ReadFile(content, path_);
+      }
     };
   }
 }
--- a/NEWS	Fri Jul 25 16:13:36 2014 +0200
+++ b/NEWS	Fri Jul 25 16:59:33 2014 +0200
@@ -8,7 +8,12 @@
 * Access patient module at the study level to cope with PatientID collisions
 * On-the-fly conversion of JSON to XML according to the HTTP Accept header
 * C-Echo SCU in the REST API
+
+Lua scripts
+-----------
+
 * Lua scripts can do HTTP requests, and thus can call Web services
+* Lua scripts can invoke system commands, with CallSystem()
 
 Plugins
 -------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Scheduler/CallSystemCommand.cpp	Fri Jul 25 16:59:33 2014 +0200
@@ -0,0 +1,72 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CallSystemCommand.h"
+
+#include <glog/logging.h>
+#include "../../Core/Toolbox.h"
+#include "../../Core/Uuid.h"
+
+namespace Orthanc
+{
+  CallSystemCommand::CallSystemCommand(ServerContext& context,
+                                       const std::string& command,
+                                       const std::vector<std::string>& arguments) : 
+    context_(context),
+    command_(command),
+    arguments_(arguments)
+  {
+  }
+
+  bool CallSystemCommand::Apply(ListOfStrings& outputs,
+                                const ListOfStrings& inputs)
+  {
+    for (ListOfStrings::const_iterator
+           it = inputs.begin(); it != inputs.end(); ++it)
+    {
+      LOG(INFO) << "Calling system command " << command_ << " on instance " << *it;
+
+      std::string dicom;
+      context_.ReadFile(dicom, *it, FileContentType_Dicom);
+
+      Toolbox::TemporaryFile tmp;
+      tmp.Write(dicom);
+
+      std::vector<std::string> args = arguments_;
+      args.push_back(tmp.GetPath());
+
+      Toolbox::ExecuteSystemCommand(command_, args);
+    }
+
+    return true;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Scheduler/CallSystemCommand.h	Fri Jul 25 16:59:33 2014 +0200
@@ -0,0 +1,55 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
+ * Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IServerCommand.h"
+#include "../ServerContext.h"
+
+namespace Orthanc
+{
+  class CallSystemCommand : public IServerCommand
+  {
+  private:
+    ServerContext& context_;
+    std::string command_;
+    std::vector<std::string> arguments_;
+
+  public:
+    CallSystemCommand(ServerContext& context,
+                      const std::string& command,
+                      const std::vector<std::string>& arguments);
+
+    virtual bool Apply(ListOfStrings& outputs,
+                       const ListOfStrings& inputs);
+  };
+}
--- a/OrthancServer/ServerContext.cpp	Fri Jul 25 16:13:36 2014 +0200
+++ b/OrthancServer/ServerContext.cpp	Fri Jul 25 16:59:33 2014 +0200
@@ -44,6 +44,7 @@
 #include <dcmtk/dcmdata/dcfilefo.h>
 
 
+#include "Scheduler/CallSystemCommand.h"
 #include "Scheduler/DeleteInstanceCommand.h"
 #include "Scheduler/ModifyInstanceCommand.h"
 #include "Scheduler/StoreScuCommand.h"
@@ -160,6 +161,47 @@
       return command.release();
     }
 
+    if (operation == "call-system")
+    {
+      LOG(INFO) << "Lua script to call system command on " << parameters["Instance"].asString();
+
+      const Json::Value& argsIn = parameters["Arguments"];
+      if (argsIn.type() != Json::arrayValue)
+      {
+        throw OrthancException(ErrorCode_BadParameterType);
+      }
+
+      std::vector<std::string> args;
+      args.reserve(argsIn.size());
+      for (Json::Value::ArrayIndex i = 0; i < argsIn.size(); ++i)
+      {
+        // http://jsoncpp.sourceforge.net/namespace_json.html#7d654b75c16a57007925868e38212b4e
+        switch (argsIn[i].type())
+        {
+          case Json::stringValue:
+            args.push_back(argsIn[i].asString());
+            break;
+
+          case Json::intValue:
+            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asInt()));
+            break;
+
+          case Json::uintValue:
+            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asUInt()));
+            break;
+
+          case Json::realValue:
+            args.push_back(boost::lexical_cast<std::string>(argsIn[i].asFloat()));
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_BadParameterType);
+        }
+      }
+
+      return new CallSystemCommand(context, parameters["Command"].asString(), args);
+    }
+
     throw OrthancException(ErrorCode_ParameterOutOfRange);
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/Samples/Lua/CallDcm2Xml.lua	Fri Jul 25 16:59:33 2014 +0200
@@ -0,0 +1,8 @@
+function OnStoredInstance(instanceId, tags, metadata)
+   -- Assume Latin1 encoding in dcm2xml
+   local args = {}
+   table.insert(args, '+Ca')
+   table.insert(args, 'latin-1')
+
+   Delete(CallSystem(instanceId, 'dcm2xml', args))
+end
--- a/Resources/Toolbox.lua	Fri Jul 25 16:13:36 2014 +0200
+++ b/Resources/Toolbox.lua	Fri Jul 25 16:59:33 2014 +0200
@@ -93,5 +93,20 @@
 end
 
 
+function CallSystem(instanceId, command, args)
+   if instanceId == nil then
+      error('Cannot modify a nonexistent instance')
+   end
+
+   table.insert(_job, { 
+                   Operation = 'call-system', 
+                   Instance = instanceId,
+                   Command = command,
+                   Arguments = args
+                })
+
+   return instanceId
+end
+
 
 print('Lua toolbox installed')