]>
git.ipfire.org Git - thirdparty/pdns.git/blob - modules/pipebackend/coprocess.cc
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 "coprocess.hh"
32 #include <sys/types.h>
34 #include "pdns/utility.hh"
36 #include "pdns/misc.hh"
37 #include "pdns/pdnsexception.hh"
40 #include <boost/algorithm/string.hpp>
43 CoProcess::CoProcess(const string
&command
,int timeout
, int infd
, int outfd
)
46 split(v
, command
, is_any_of(" "));
48 std::vector
<const char *>argv(v
.size()+1);
51 for (size_t n
= 0; n
< v
.size(); n
++)
53 // we get away with not copying since nobody resizes v
54 launch(argv
.data(), timeout
, infd
, outfd
);
57 void CoProcess::launch(const char **argv
, int timeout
, int infd
, int outfd
)
63 signal(SIGPIPE
, SIG_IGN
);
65 if(access(argv
[0],X_OK
)) // check before fork so we can throw
66 throw PDNSException("Command '"+string(argv
[0])+"' cannot be executed: "+stringerror());
68 if(pipe(d_fd1
)<0 || pipe(d_fd2
)<0)
69 throw PDNSException("Unable to open pipe for coprocess: "+string(strerror(errno
)));
72 throw PDNSException("Unable to fork for coprocess: "+stringerror());
73 else if(d_pid
>0) { // parent speaking
75 setCloseOnExec(d_fd1
[1]);
77 setCloseOnExec(d_fd2
[0]);
78 if(!(d_fp
=fdopen(d_fd2
[0],"r")))
79 throw PDNSException("Unable to associate a file pointer with pipe: "+stringerror());
81 setbuf(d_fp
,0); // no buffering please, confuses select
83 else if(!d_pid
) { // child
84 signal(SIGCHLD
, SIG_DFL
); // silence a warning from perl
93 if(d_fd2
[1]!= outfd
) {
94 dup2(d_fd2
[1], outfd
);
98 // stdin & stdout are now connected, fire up our coprocess!
100 if(execv(argv
[0], const_cast<char * const *>(argv
))<0) // now what
103 /* not a lot we can do here. We shouldn't return because that will leave a forked process around.
104 no way to log this either - only thing we can do is make sure that our parent catches this soonest! */
108 CoProcess::~CoProcess()
111 if(!waitpid(d_pid
, &status
, WNOHANG
)) {
113 waitpid(d_pid
, &status
, 0);
120 void CoProcess::checkStatus()
123 int ret
=waitpid(d_pid
, &status
, WNOHANG
);
125 throw PDNSException("Unable to ascertain status of coprocess "+itoa(d_pid
)+" from "+itoa(getpid())+": "+string(strerror(errno
)));
127 if(WIFEXITED(status
)) {
128 int exitStatus
=WEXITSTATUS(status
);
129 throw PDNSException("Coprocess exited with code "+itoa(exitStatus
));
131 if(WIFSIGNALED(status
)) {
132 int sig
=WTERMSIG(status
);
133 string reason
="CoProcess died on receiving signal "+itoa(sig
);
135 if(WCOREDUMP(status
))
136 reason
+=". Dumped core";
139 throw PDNSException(reason
);
144 void CoProcess::send(const string
&snd
)
153 // writen routine - socket may not accept al data in one go
154 while(sent
<line
.size()) {
155 bytes
=write(d_fd1
[1],line
.c_str()+sent
,line
.length()-sent
);
157 throw PDNSException("Writing to coprocess failed: "+string(strerror(errno
)));
163 void CoProcess::receive(string
&receive
)
169 tv
.tv_sec
=d_timeout
/1000;
170 tv
.tv_usec
=(d_timeout
% 1000) * 1000;
174 FD_SET(fileno(d_fp
),&rds
);
175 int ret
=select(fileno(d_fp
)+1,&rds
,0,0,&tv
);
177 throw PDNSException("Error waiting on data from coprocess: "+stringerror());
179 throw PDNSException("Timeout waiting for data from coprocess");
182 if(!stringfgets(d_fp
, receive
))
183 throw PDNSException("Child closed pipe");
188 void CoProcess::sendReceive(const string
&snd
, string
&rcv
)
196 UnixRemote::UnixRemote(const string
& path
, int timeout
)
198 d_fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
200 throw PDNSException("Unable to create UNIX domain socket: "+string(strerror(errno
)));
202 struct sockaddr_un remote
;
203 if (makeUNsockaddr(path
, &remote
))
204 throw PDNSException("Unable to create UNIX domain socket: Path '"+path
+"' is not a valid UNIX socket path.");
206 // fcntl(fd, F_SETFL, O_NONBLOCK, &sock);
208 if(connect(d_fd
, (struct sockaddr
*)&remote
, sizeof(remote
)) < 0)
209 unixDie("Unable to connect to remote '"+path
+"' using UNIX domain socket");
211 d_fp
= fdopen(d_fd
, "r");
214 UnixRemote::~UnixRemote()
219 void UnixRemote::send(const string
& line
)
222 nline
.append(1, '\n');
223 writen2(d_fd
, nline
);
226 void UnixRemote::receive(string
& line
)
229 stringfgets(d_fp
, line
);
233 void UnixRemote::sendReceive(const string
&snd
, string
&rcv
)
240 bool isUnixSocket(const string
& fname
)
243 if(stat(fname
.c_str(), &st
) < 0)
244 return false; // not a unix socket in any case ;-)
246 return (st
.st_mode
& S_IFSOCK
) == S_IFSOCK
;
254 CoProcess
cp("./irc.pl");
256 cp
.sendReceive("www.trilab.com", reply
);
257 cout
<<"Answered: '"<<reply
<<"'"<<endl
;
259 catch(PDNSException
&ae
) {
260 cerr
<<ae
.reason
<<endl
;