Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2018-2023 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 : #include <sys/wait.h> 29 : #endif 30 : 31 : namespace PLMD { 32 : 33 : /// Retrieve PLUMED_ENABLE_SIGNALS. 34 : /// Inline static so that it can store a static variable (for quicker access) 35 : /// without adding a unique global symbol to a library including this header file. 36 34 : inline static bool SubprocessPidGetenvSignals() noexcept { 37 34 : static const bool res=std::getenv("PLUMED_ENABLE_SIGNALS"); 38 34 : return res; 39 : } 40 : 41 : /// Small utility class, used to avoid inclusion of unistd.h> in a header file. 42 : class SubprocessPid { 43 : #ifdef __PLUMED_HAS_SUBPROCESS 44 : public: 45 : pid_t pid; 46 10002 : explicit SubprocessPid(pid_t pid): 47 10002 : pid(pid) { 48 10002 : plumed_assert(pid!=0 && pid!=-1); 49 10002 : } 50 18 : void stop() noexcept { 51 : // Signals give problems with MPI on Travis. 52 : // I disable them for now. 53 18 : if(SubprocessPidGetenvSignals()) 54 0 : if(pid!=0 && pid!=-1) { 55 0 : kill(pid,SIGSTOP); 56 : } 57 18 : } 58 16 : void cont() noexcept { 59 : // Signals give problems with MPI on Travis. 60 : // I disable them for now. 61 16 : if(SubprocessPidGetenvSignals()) 62 0 : if(pid!=0 && pid!=-1) { 63 0 : kill(pid,SIGCONT); 64 : } 65 16 : } 66 10002 : ~SubprocessPid() { 67 : // this is apparently working also with MPI on Travis. 68 10002 : if(pid!=0 && pid!=-1) { 69 : int status; 70 10002 : kill(pid,SIGINT); 71 10002 : waitpid(pid, &status, 0); // Wait for the child process to terminate 72 : } 73 10002 : } 74 : #endif 75 : }; 76 : 77 10002 : Subprocess::Subprocess(const std::string & cmd) { 78 : #ifdef __PLUMED_HAS_SUBPROCESS 79 10002 : char* arr [] = { 80 : // const_cast are necessary here due to the declaration of execv 81 : const_cast<char*>("/bin/sh"), 82 : const_cast<char*>("-c"), 83 : const_cast<char*>(cmd.c_str()), 84 : nullptr 85 10002 : }; 86 : int cp[2]; 87 : int pc[2]; 88 10002 : if(pipe(pc)<0) { 89 0 : plumed_error()<<"error creating parent to child pipe"; 90 : } 91 10002 : if(pipe(cp)<0) { 92 0 : plumed_error()<<"error creating child to parent pipe"; 93 : } 94 10002 : pid_t pid=fork(); 95 10002 : switch(pid) { 96 0 : case -1: 97 0 : plumed_error()<<"error forking"; 98 : break; 99 : // CHILD: 100 0 : case 0: { 101 0 : if(close(1)<0) { 102 0 : plumed_error()<<"error closing file"; 103 : } 104 0 : if(dup(cp[1])<0) { 105 0 : plumed_error()<<"error duplicating file"; 106 : } 107 0 : if(close(0)<0) { 108 0 : plumed_error()<<"error closing file"; 109 : } 110 0 : if(dup(pc[0])<0) { 111 0 : plumed_error()<<"error duplicating file"; 112 : } 113 0 : if(close(pc[1])<0) { 114 0 : plumed_error()<<"error closing file"; 115 : } 116 0 : if(close(cp[0])<0) { 117 0 : plumed_error()<<"error closing file"; 118 : } 119 0 : execv(arr[0],arr); 120 0 : plumed_error()<<"error in script file"; 121 : } 122 : // PARENT:: 123 10002 : default: 124 20004 : this->pid=Tools::make_unique<SubprocessPid>(pid); 125 10002 : if(close(pc[0])<0) { 126 0 : plumed_error()<<"error closing file"; 127 : } 128 10002 : if(close(cp[1])<0) { 129 0 : plumed_error()<<"error closing file"; 130 : } 131 10002 : fpc=pc[1]; 132 10002 : fcp=cp[0]; 133 10002 : fppc=fdopen(fpc,"w"); 134 10002 : parent_to_child.link(fppc); 135 10002 : fpcp=fdopen(fcp,"r"); 136 10002 : child_to_parent.link(fpcp); 137 : } 138 : #else 139 : plumed_error()<<"Subprocess not supported"; 140 : #endif 141 10002 : } 142 : 143 10002 : Subprocess::~Subprocess() { 144 : #ifdef __PLUMED_HAS_SUBPROCESS 145 : // close files: 146 10002 : fclose(fppc); 147 10002 : fclose(fpcp); 148 : // fclose also closes the underlying descriptors, 149 : // so this is not needed: 150 10002 : close(fpc); 151 10002 : close(fcp); 152 : // after closing the communication, the subprocess is killed 153 : // in pid's destructor 154 : #endif 155 10002 : } 156 : 157 108 : bool Subprocess::available() noexcept { 158 : #ifdef __PLUMED_HAS_SUBPROCESS 159 108 : return true; 160 : #else 161 : return false; 162 : #endif 163 : } 164 : 165 18 : void Subprocess::stop() noexcept { 166 : #ifdef __PLUMED_HAS_SUBPROCESS 167 18 : pid->stop(); 168 : #endif 169 18 : } 170 : 171 16 : void Subprocess::cont() noexcept { 172 : #ifdef __PLUMED_HAS_SUBPROCESS 173 : pid->cont(); 174 : #endif 175 16 : } 176 : 177 16 : void Subprocess::flush() { 178 16 : parent_to_child.flush(); 179 16 : } 180 : 181 16 : Subprocess & Subprocess::getline(std::string & line) { 182 16 : child_to_parent.getline(line); 183 16 : if(!child_to_parent) { 184 0 : plumed_error() <<"error reading subprocess"; 185 : } 186 16 : return (*this); 187 : } 188 : 189 16 : Subprocess::Handler::Handler(Subprocess *sp) noexcept: 190 16 : sp(sp) { 191 16 : sp->cont(); 192 16 : } 193 : 194 16 : Subprocess::Handler::~Handler() { 195 16 : if(sp) { 196 16 : sp->stop(); 197 : } 198 16 : } 199 : 200 0 : Subprocess::Handler::Handler(Handler && handler) noexcept : 201 0 : sp(handler.sp) { 202 0 : handler.sp=nullptr; 203 0 : } 204 : 205 0 : Subprocess::Handler & Subprocess::Handler::operator=(Handler && handler) noexcept { 206 0 : if(this!=&handler) { 207 0 : if(sp) { 208 0 : sp->stop(); 209 : } 210 0 : sp=handler.sp; 211 0 : handler.sp=nullptr; 212 : } 213 0 : return *this; 214 : } 215 : 216 : 217 : }