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 : { 49 10002 : plumed_assert(pid!=0 && pid!=-1); 50 10002 : } 51 18 : void stop() noexcept { 52 : // Signals give problems with MPI on Travis. 53 : // I disable them for now. 54 18 : if(SubprocessPidGetenvSignals()) if(pid!=0 && pid!=-1) kill(pid,SIGSTOP); 55 18 : } 56 16 : void cont() noexcept { 57 : // Signals give problems with MPI on Travis. 58 : // I disable them for now. 59 16 : if(SubprocessPidGetenvSignals()) if(pid!=0 && pid!=-1) kill(pid,SIGCONT); 60 16 : } 61 10002 : ~SubprocessPid() { 62 : // this is apparently working also with MPI on Travis. 63 10002 : if(pid!=0 && pid!=-1) { 64 : int status; 65 10002 : kill(pid,SIGINT); 66 10002 : waitpid(pid, &status, 0); // Wait for the child process to terminate 67 : } 68 10002 : } 69 : #endif 70 : }; 71 : 72 10002 : Subprocess::Subprocess(const std::string & cmd) { 73 : #ifdef __PLUMED_HAS_SUBPROCESS 74 10002 : char* arr [] = { 75 : // const_cast are necessary here due to the declaration of execv 76 : const_cast<char*>("/bin/sh"), 77 : const_cast<char*>("-c"), 78 : const_cast<char*>(cmd.c_str()), 79 : nullptr 80 10002 : }; 81 : int cp[2]; 82 : int pc[2]; 83 10002 : if(pipe(pc)<0) plumed_error()<<"error creating parent to child pipe"; 84 10002 : if(pipe(cp)<0) plumed_error()<<"error creating child to parent pipe"; 85 10002 : pid_t pid=fork(); 86 10002 : switch(pid) { 87 0 : case -1: 88 0 : plumed_error()<<"error forking"; 89 : break; 90 : // CHILD: 91 0 : case 0: 92 : { 93 0 : if(close(1)<0) plumed_error()<<"error closing file"; 94 0 : if(dup(cp[1])<0) plumed_error()<<"error duplicating file"; 95 0 : if(close(0)<0) plumed_error()<<"error closing file"; 96 0 : if(dup(pc[0])<0) plumed_error()<<"error duplicating file"; 97 0 : if(close(pc[1])<0) plumed_error()<<"error closing file"; 98 0 : if(close(cp[0])<0) plumed_error()<<"error closing file"; 99 0 : execv(arr[0],arr); 100 0 : plumed_error()<<"error in script file"; 101 : } 102 : // PARENT:: 103 10002 : default: 104 20004 : this->pid=Tools::make_unique<SubprocessPid>(pid); 105 10002 : if(close(pc[0])<0) plumed_error()<<"error closing file"; 106 10002 : if(close(cp[1])<0) plumed_error()<<"error closing file"; 107 10002 : fpc=pc[1]; 108 10002 : fcp=cp[0]; 109 10002 : fppc=fdopen(fpc,"w"); 110 10002 : parent_to_child.link(fppc); 111 10002 : fpcp=fdopen(fcp,"r"); 112 10002 : child_to_parent.link(fpcp); 113 : } 114 : #else 115 : plumed_error()<<"Subprocess not supported"; 116 : #endif 117 10002 : } 118 : 119 10002 : Subprocess::~Subprocess() { 120 : #ifdef __PLUMED_HAS_SUBPROCESS 121 : // close files: 122 10002 : fclose(fppc); 123 10002 : fclose(fpcp); 124 : // fclose also closes the underlying descriptors, 125 : // so this is not needed: 126 10002 : close(fpc); 127 10002 : close(fcp); 128 : // after closing the communication, the subprocess is killed 129 : // in pid's destructor 130 : #endif 131 10002 : } 132 : 133 83 : bool Subprocess::available() noexcept { 134 : #ifdef __PLUMED_HAS_SUBPROCESS 135 83 : return true; 136 : #else 137 : return false; 138 : #endif 139 : } 140 : 141 18 : void Subprocess::stop() noexcept { 142 : #ifdef __PLUMED_HAS_SUBPROCESS 143 18 : pid->stop(); 144 : #endif 145 18 : } 146 : 147 16 : void Subprocess::cont() noexcept { 148 : #ifdef __PLUMED_HAS_SUBPROCESS 149 16 : pid->cont(); 150 : #endif 151 16 : } 152 : 153 16 : void Subprocess::flush() { 154 16 : parent_to_child.flush(); 155 16 : } 156 : 157 16 : Subprocess & Subprocess::getline(std::string & line) { 158 16 : child_to_parent.getline(line); 159 16 : if(!child_to_parent) plumed_error() <<"error reading subprocess"; 160 16 : return (*this); 161 : } 162 : 163 16 : Subprocess::Handler::Handler(Subprocess *sp) noexcept: 164 16 : sp(sp) 165 : { 166 16 : sp->cont(); 167 16 : } 168 : 169 16 : Subprocess::Handler::~Handler() { 170 16 : if(sp) sp->stop(); 171 16 : } 172 : 173 0 : Subprocess::Handler::Handler(Handler && handler) noexcept : 174 0 : sp(handler.sp) 175 : { 176 0 : handler.sp=nullptr; 177 0 : } 178 : 179 0 : Subprocess::Handler & Subprocess::Handler::operator=(Handler && handler) noexcept { 180 0 : if(this!=&handler) { 181 0 : if(sp) sp->stop(); 182 0 : sp=handler.sp; 183 0 : handler.sp=nullptr; 184 : } 185 0 : return *this; 186 : } 187 : 188 : 189 : }