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