# HG changeset patch # User Sebastien Jodogne # Date 1406300373 -7200 # Node ID 921532f677701bd5f85ee5996f199a98024538f6 # Parent cd20e2568fc27a7d6f28959c9e36a51e1278b8a5 Lua scripts can invoke system commands, with CallSystem() diff -r cd20e2568fc2 -r 921532f67770 CMakeLists.txt --- 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 ) diff -r cd20e2568fc2 -r 921532f67770 Core/Uuid.h --- 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_); + } }; } } diff -r cd20e2568fc2 -r 921532f67770 NEWS --- 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 ------- diff -r cd20e2568fc2 -r 921532f67770 OrthancServer/Scheduler/CallSystemCommand.cpp --- /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 . + **/ + + +#include "CallSystemCommand.h" + +#include +#include "../../Core/Toolbox.h" +#include "../../Core/Uuid.h" + +namespace Orthanc +{ + CallSystemCommand::CallSystemCommand(ServerContext& context, + const std::string& command, + const std::vector& 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 args = arguments_; + args.push_back(tmp.GetPath()); + + Toolbox::ExecuteSystemCommand(command_, args); + } + + return true; + } +} diff -r cd20e2568fc2 -r 921532f67770 OrthancServer/Scheduler/CallSystemCommand.h --- /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 . + **/ + + +#pragma once + +#include "IServerCommand.h" +#include "../ServerContext.h" + +namespace Orthanc +{ + class CallSystemCommand : public IServerCommand + { + private: + ServerContext& context_; + std::string command_; + std::vector arguments_; + + public: + CallSystemCommand(ServerContext& context, + const std::string& command, + const std::vector& arguments); + + virtual bool Apply(ListOfStrings& outputs, + const ListOfStrings& inputs); + }; +} diff -r cd20e2568fc2 -r 921532f67770 OrthancServer/ServerContext.cpp --- 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 +#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 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(argsIn[i].asInt())); + break; + + case Json::uintValue: + args.push_back(boost::lexical_cast(argsIn[i].asUInt())); + break; + + case Json::realValue: + args.push_back(boost::lexical_cast(argsIn[i].asFloat())); + break; + + default: + throw OrthancException(ErrorCode_BadParameterType); + } + } + + return new CallSystemCommand(context, parameters["Command"].asString(), args); + } + throw OrthancException(ErrorCode_ParameterOutOfRange); } diff -r cd20e2568fc2 -r 921532f67770 Resources/Samples/Lua/CallDcm2Xml.lua --- /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 diff -r cd20e2568fc2 -r 921532f67770 Resources/Toolbox.lua --- 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')