]>
git.ipfire.org Git - thirdparty/pdns.git/blob - modules/remotebackend/pipeconnector.cc
b7fd605ee525793079895461d0e8a8ce7165be41
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "remotebackend.hh"
27 PipeConnector::PipeConnector(std::map
<std::string
,std::string
> optionsMap
) {
28 if (optionsMap
.count("command") == 0) {
29 L
<<Logger::Error
<<"Cannot find 'command' option in connection string"<<endl
;
30 throw PDNSException();
32 this->command
= optionsMap
.find("command")->second
;
33 this->options
= optionsMap
;
36 if (optionsMap
.find("timeout") != optionsMap
.end()) {
37 d_timeout
= std::stoi(optionsMap
.find("timeout")->second
);
42 d_fd1
[0] = d_fd1
[1] = -1;
43 d_fd2
[0] = d_fd2
[1] = -1;
46 PipeConnector::~PipeConnector(){
49 if (d_pid
== -1) return;
51 if(!waitpid(d_pid
, &status
, WNOHANG
)) {
53 waitpid(d_pid
, &status
, 0);
57 if (d_fp
!= NULL
) fclose(d_fp
);
60 void PipeConnector::launch() {
62 if (d_pid
> 0 && checkStatus()) return;
64 std::vector
<std::string
> v
;
65 split(v
, command
, is_any_of(" "));
67 std::vector
<const char *>argv(v
.size()+1);
70 for (size_t n
= 0; n
< v
.size(); n
++)
73 signal(SIGPIPE
, SIG_IGN
);
75 if(access(argv
[0],X_OK
)) // check before fork so we can throw
76 throw PDNSException("Command '"+string(argv
[0])+"' cannot be executed: "+stringerror());
78 if(pipe(d_fd1
)<0 || pipe(d_fd2
)<0)
79 throw PDNSException("Unable to open pipe for coprocess: "+string(strerror(errno
)));
82 throw PDNSException("Unable to fork for coprocess: "+stringerror());
83 else if(d_pid
>0) { // parent speaking
85 setCloseOnExec(d_fd1
[1]);
87 setCloseOnExec(d_fd2
[0]);
88 if(!(d_fp
=fdopen(d_fd2
[0],"r")))
89 throw PDNSException("Unable to associate a file pointer with pipe: "+stringerror());
91 setbuf(d_fp
,0); // no buffering please, confuses select
93 else if(!d_pid
) { // child
94 signal(SIGCHLD
, SIG_DFL
); // silence a warning from perl
108 // stdin & stdout are now connected, fire up our coprocess!
110 if(execv(argv
[0], const_cast<char * const *>(argv
.data()))<0) // now what
113 /* not a lot we can do here. We shouldn't return because that will leave a forked process around.
114 no way to log this either - only thing we can do is make sure that our parent catches this soonest! */
117 Json::array parameters
;
118 Json msg
= Json(Json::object
{
119 { "method", "initialize" },
120 { "parameters", Json(options
) },
125 if (this->recv(msg
)==false) {
126 L
<<Logger::Error
<<"Failed to initialize coprocess"<<std::endl
;
130 int PipeConnector::send_message(const Json
& input
)
132 auto line
= input
.dump();
140 // writen routine - socket may not accept al data in one go
141 while(sent
<line
.size()) {
142 bytes
=write(d_fd1
[1],line
.c_str()+sent
,line
.length()-sent
);
144 throw PDNSException("Writing to coprocess failed: "+std::string(strerror(errno
)));
151 int PipeConnector::recv_message(Json
& output
)
155 std::string s_output
;
162 tv
.tv_sec
= d_timeout
/1000;
163 tv
.tv_usec
= (d_timeout
% 1000) * 1000;
166 FD_SET(fileno(d_fp
),&rds
);
167 int ret
=select(fileno(d_fp
)+1,&rds
,0,0,&tv
);
169 throw PDNSException("Error waiting on data from coprocess: "+stringerror());
171 throw PDNSException("Timeout waiting for data from coprocess");
174 if(!stringfgets(d_fp
, receive
))
175 throw PDNSException("Child closed pipe");
177 s_output
.append(receive
);
178 // see if it can be parsed
179 output
= Json::parse(s_output
, err
);
180 if (output
!= nullptr) return s_output
.size();
185 bool PipeConnector::checkStatus()
188 int ret
=waitpid(d_pid
, &status
, WNOHANG
);
190 throw PDNSException("Unable to ascertain status of coprocess "+itoa(d_pid
)+" from "+itoa(getpid())+": "+string(strerror(errno
)));
192 if(WIFEXITED(status
)) {
193 int exitStatus
=WEXITSTATUS(status
);
194 throw PDNSException("Coprocess exited with code "+itoa(exitStatus
));
196 if(WIFSIGNALED(status
)) {
197 int sig
=WTERMSIG(status
);
198 string reason
="CoProcess died on receiving signal "+itoa(sig
);
200 if(WCOREDUMP(status
))
201 reason
+=". Dumped core";
204 throw PDNSException(reason
);