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