Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2019 The plumed team 3 : (see the PEOPLE file at the root of the distribution for a list of names) 4 : 5 : See http://www.plumed.org for more information. 6 : 7 : This file is part of plumed, version 2. 8 : 9 : plumed is free software: you can redistribute it and/or modify 10 : it under the terms of the GNU Lesser General Public License as published by 11 : the Free Software Foundation, either version 3 of the License, or 12 : (at your option) any later version. 13 : 14 : plumed is distributed in the hope that it will be useful, 15 : but WITHOUT ANY WARRANTY; without even the implied warranty of 16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 : GNU Lesser General Public License for more details. 18 : 19 : You should have received a copy of the GNU Lesser General Public License 20 : along with plumed. If not, see <http://www.gnu.org/licenses/>. 21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 22 : #include "Subprocess.h" 23 : #include "Exception.h" 24 : #include "Tools.h" 25 : #ifdef __PLUMED_HAS_SUBPROCESS 26 : #include <unistd.h> 27 : #include <csignal> 28 : #endif 29 : 30 : using namespace std; 31 : namespace PLMD { 32 : 33 : /// Small utility class, used to avoid inclusion of unistd.h> in a header file. 34 : class SubprocessPid { 35 17 : static bool signals() noexcept { 36 17 : static const bool res=std::getenv("PLUMED_ENABLE_SIGNALS"); 37 17 : return res; 38 : } 39 : #ifdef __PLUMED_HAS_SUBPROCESS 40 : public: 41 : pid_t pid; 42 1 : explicit SubprocessPid(pid_t pid): 43 1 : pid(pid) 44 : { 45 1 : plumed_assert(pid!=0 && pid!=-1); 46 1 : } 47 9 : void stop() noexcept { 48 : // signals give problems with MPI on Travis. 49 : // I disable them for now. 50 9 : if(signals()) if(pid!=0 && pid!=-1) kill(pid,SIGSTOP); 51 9 : } 52 8 : void cont() noexcept { 53 : // signals give problems with MPI on Travis. 54 : // I disable them for now. 55 8 : if(signals()) if(pid!=0 && pid!=-1) kill(pid,SIGCONT); 56 8 : } 57 : ~SubprocessPid() { 58 : // this is apparently working also with MPI on Travis. 59 1 : if(pid!=0 && pid!=-1) kill(pid,SIGINT); 60 : } 61 : #endif 62 : }; 63 : 64 1 : Subprocess::Subprocess(const std::string & cmd) { 65 : #ifdef __PLUMED_HAS_SUBPROCESS 66 : char* arr [] = { 67 : // const_cast are necessary here due to the declaration of execv 68 : const_cast<char*>("/bin/sh"), 69 : const_cast<char*>("-c"), 70 : const_cast<char*>(cmd.c_str()), 71 : nullptr 72 2 : }; 73 : int cp[2]; 74 : int pc[2]; 75 1 : if(pipe(pc)<0) plumed_error()<<"error creating parent to child pipe"; 76 1 : if(pipe(cp)<0) plumed_error()<<"error creating child to parent pipe"; 77 1 : pid_t pid=fork(); 78 1 : switch(pid) { 79 : case -1: 80 0 : plumed_error()<<"error forking"; 81 : break; 82 : // CHILD: 83 : case 0: 84 : { 85 0 : if(close(1)<0) plumed_error()<<"error closing file"; 86 0 : if(dup(cp[1])<0) plumed_error()<<"error duplicating file"; 87 0 : if(close(0)<0) plumed_error()<<"error closing file"; 88 0 : if(dup(pc[0])<0) plumed_error()<<"error duplicating file"; 89 0 : if(close(pc[1])<0) plumed_error()<<"error closing file"; 90 0 : if(close(cp[0])<0) plumed_error()<<"error closing file"; 91 0 : execv(arr[0],arr); 92 0 : plumed_error()<<"error in script file"; 93 : } 94 : // PARENT:: 95 : default: 96 1 : this->pid.reset(new SubprocessPid(pid)); 97 1 : if(close(pc[0])<0) plumed_error()<<"error closing file"; 98 1 : if(close(cp[1])<0) plumed_error()<<"error closing file"; 99 1 : fpc=pc[1]; 100 1 : fcp=cp[0]; 101 1 : fppc=fdopen(fpc,"w"); 102 1 : parent_to_child.link(fppc); 103 1 : fpcp=fdopen(fcp,"r"); 104 1 : child_to_parent.link(fpcp); 105 : } 106 : #else 107 : plumed_error()<<"Subprocess not supported"; 108 : #endif 109 1 : } 110 : 111 2 : Subprocess::~Subprocess() { 112 : #ifdef __PLUMED_HAS_SUBPROCESS 113 : // fpc should be closed to terminate the child executable 114 1 : fclose(fppc); 115 1 : close(fpc); 116 : // fcp should not be closed because it could make the child executable fail 117 : /// TODO: check if this is necessary and make this class exception safe! 118 : #endif 119 1 : } 120 : 121 73 : bool Subprocess::available() noexcept { 122 : #ifdef __PLUMED_HAS_SUBPROCESS 123 73 : return true; 124 : #else 125 : return false; 126 : #endif 127 : } 128 : 129 9 : void Subprocess::stop() noexcept { 130 : #ifdef __PLUMED_HAS_SUBPROCESS 131 9 : pid->stop(); 132 : #endif 133 9 : } 134 : 135 8 : void Subprocess::cont() noexcept { 136 : #ifdef __PLUMED_HAS_SUBPROCESS 137 8 : pid->cont(); 138 : #endif 139 8 : } 140 : 141 8 : void Subprocess::flush() { 142 8 : parent_to_child.flush(); 143 8 : } 144 : 145 8 : Subprocess & Subprocess::getline(std::string & line) { 146 8 : child_to_parent.getline(line); 147 8 : if(!child_to_parent) plumed_error() <<"error reading subprocess"; 148 8 : return (*this); 149 : } 150 : 151 8 : Subprocess::Handler::Handler(Subprocess *sp) noexcept: 152 8 : sp(sp) 153 : { 154 8 : sp->cont(); 155 8 : } 156 : 157 8 : Subprocess::Handler::~Handler() { 158 8 : if(sp) sp->stop(); 159 8 : } 160 : 161 0 : Subprocess::Handler::Handler(Handler && handler) noexcept : 162 0 : sp(handler.sp) 163 : { 164 0 : handler.sp=nullptr; 165 0 : } 166 : 167 0 : Subprocess::Handler & Subprocess::Handler::operator=(Handler && handler) noexcept { 168 0 : if(this!=&handler) { 169 0 : if(sp) sp->stop(); 170 0 : sp=handler.sp; 171 0 : handler.sp=nullptr; 172 : } 173 0 : return *this; 174 : } 175 : 176 : 177 : }