• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 19278070070

11 Nov 2025 08:45PM UTC coverage: 73.0% (-0.006%) from 73.006%
19278070070

Pull #16401

github

web-flow
Merge 13be29806 into 7d2f921c4
Pull Request #16401: auth meson: reset binary names to original

38276 of 63152 branches covered (60.61%)

Branch coverage included in aggregate %.

127560 of 164020 relevant lines covered (77.77%)

4963065.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.91
/modules/pipebackend/coprocess.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
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.
8
 *
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.
12
 *
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.
17
 *
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.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "coprocess.hh"
26
#include <stdlib.h>
27
#include <unistd.h>
28
#include <string>
29
#include <cerrno>
30
#include <csignal>
31
#include <string.h>
32
#include <sys/types.h>
33
#include <sys/wait.h>
34
#include "pdns/utility.hh"
35
#include <sys/un.h>
36
#include "pdns/misc.hh"
37
#include "pdns/pdnsexception.hh"
38
#include <sys/stat.h>
39
#include <unistd.h>
40
#include <boost/algorithm/string.hpp>
41
#include <vector>
42

43
CoProcess::CoProcess(const string& command, int timeout, int infd, int outfd) :
44
  d_infd(infd), d_outfd(outfd), d_timeout(timeout)
45
{
10✔
46
  split(d_params, command, boost::is_any_of(" "));
10✔
47

48
  d_argv.resize(d_params.size() + 1);
10✔
49
  d_argv[d_params.size()] = nullptr;
10✔
50

51
  for (size_t n = 0; n < d_params.size(); n++) {
20✔
52
    d_argv[n] = d_params[n].c_str();
10✔
53
  }
10✔
54
  d_pid = 0;
10✔
55
}
10✔
56

57
void CoProcess::launch()
58
{
10✔
59
  signal(SIGPIPE, SIG_IGN);
10✔
60

61
  if (access(d_argv[0], X_OK)) // check before fork so we can throw
10!
62
    throw PDNSException("Command '" + string(d_argv[0]) + "' cannot be executed: " + stringerror());
×
63

64
  if (pipe(d_fd1) < 0 || pipe(d_fd2) < 0)
10!
65
    throw PDNSException("Unable to open pipe for coprocess: " + string(strerror(errno)));
×
66

67
  if ((d_pid = fork()) < 0)
10!
68
    throw PDNSException("Unable to fork for coprocess: " + stringerror());
×
69
  else if (d_pid > 0) { // parent speaking
10!
70
    // no need to keep this around
71
    d_argv.clear();
10✔
72
    close(d_fd1[0]);
10✔
73
    setCloseOnExec(d_fd1[1]);
10✔
74
    close(d_fd2[1]);
10✔
75
    setCloseOnExec(d_fd2[0]);
10✔
76

77
    if (d_timeout) {
10!
78
      setNonBlocking(d_fd2[0]);
10✔
79
    }
10✔
80
  }
10✔
81
  else if (!d_pid) { // child
×
82
    signal(SIGCHLD, SIG_DFL); // silence a warning from perl
×
83
    close(d_fd1[1]);
×
84
    close(d_fd2[0]);
×
85

86
    if (d_fd1[0] != d_infd) {
×
87
      dup2(d_fd1[0], d_infd);
×
88
      close(d_fd1[0]);
×
89
    }
×
90

91
    if (d_fd2[1] != d_outfd) {
×
92
      dup2(d_fd2[1], d_outfd);
×
93
      close(d_fd2[1]);
×
94
    }
×
95

96
    // stdin & stdout are now connected, fire up our coprocess!
97
    if (execv(d_argv[0], const_cast<char* const*>(d_argv.data())) < 0) // now what
×
98
      exit(123);
×
99

100
    /* not a lot we can do here. We shouldn't return because that will leave a forked process around.
101
       no way to log this either - only thing we can do is make sure that our parent catches this soonest! */
102
  }
×
103
}
10✔
104

105
CoProcess::~CoProcess()
106
{
2✔
107
  int status;
2✔
108
  if (d_pid) {
2!
109
    if (!waitpid(d_pid, &status, WNOHANG)) {
2!
110
      kill(d_pid, 9);
2✔
111
      waitpid(d_pid, &status, 0);
2✔
112
    }
2✔
113
  }
2✔
114

115
  close(d_fd1[1]);
2✔
116
  close(d_fd2[0]);
2✔
117
}
2✔
118

119
void CoProcess::checkStatus()
120
{
27✔
121
  int status;
27✔
122
  int ret = waitpid(d_pid, &status, WNOHANG);
27✔
123
  if (ret < 0)
27!
124
    throw PDNSException("Unable to ascertain status of coprocess " + std::to_string(d_pid) + " from " + std::to_string(getpid()) + ": " + string(strerror(errno)));
×
125
  else if (ret) {
27!
126
    if (WIFEXITED(status)) {
×
127
      int exitStatus = WEXITSTATUS(status);
×
128
      throw PDNSException("Coprocess exited with code " + std::to_string(exitStatus));
×
129
    }
×
130
    if (WIFSIGNALED(status)) {
×
131
      int sig = WTERMSIG(status);
×
132
      string reason = "CoProcess died on receiving signal " + std::to_string(sig);
×
133
#ifdef WCOREDUMP
×
134
      if (WCOREDUMP(status))
×
135
        reason += ". Dumped core";
×
136
#endif
×
137

138
      throw PDNSException(reason);
×
139
    }
×
140
  }
×
141
}
27✔
142

143
void CoProcess::send(const string& snd)
144
{
27✔
145
  checkStatus();
27✔
146
  string line(snd);
27✔
147
  line.append(1, '\n');
27✔
148

149
  unsigned int sent = 0;
27✔
150
  int bytes;
27✔
151

152
  // writen routine - socket may not accept al data in one go
153
  while (sent < line.size()) {
54✔
154
    bytes = write(d_fd1[1], line.c_str() + sent, line.length() - sent);
27✔
155
    if (bytes < 0)
27!
156
      throw PDNSException("Writing to coprocess failed: " + string(strerror(errno)));
×
157

158
    sent += bytes;
27✔
159
  }
27✔
160
}
27✔
161

162
void CoProcess::receive(string& received)
163
{
37✔
164
  received.clear();
37✔
165

166
  // we might still have some remaining data from our last read
167
  if (!d_remaining.empty()) {
37✔
168
    received = std::move(d_remaining);
10✔
169
  }
10✔
170

171
  size_t lastPos = 0;
37✔
172
  size_t eolPos;
37✔
173
  while ((eolPos = received.find('\n', lastPos)) == std::string::npos) {
92✔
174
    size_t existingSize = received.size();
55✔
175
    lastPos = existingSize;
55✔
176
    received.resize(existingSize + 4096);
55✔
177
    ssize_t got = read(d_fd2[0], &received.at(existingSize), 4096);
55✔
178
    if (got == 0) {
55!
179
      throw PDNSException("Child closed pipe");
×
180
    }
×
181
    else if (got < 0) {
55✔
182
      received.resize(existingSize);
27✔
183
      int saved = errno;
27✔
184
      if (saved == EINTR) {
27!
185
        continue;
×
186
      }
×
187
      if (saved == EAGAIN) {
27!
188
        if (d_timeout) {
27!
189
          int ret = waitForData(d_fd2[0], 0, d_timeout);
27✔
190
          if (ret < 0)
27!
191
            throw PDNSException("Error waiting on data from coprocess: " + string(strerror(saved)));
×
192
          if (!ret)
27!
193
            throw PDNSException("Timeout waiting for data from coprocess");
×
194
        }
27✔
195
      }
27✔
196
      else {
×
197
        throw PDNSException("Error reading from child's pipe:" + string(strerror(saved)));
×
198
      }
×
199
    }
27✔
200
    else {
28✔
201
      received.resize(existingSize + static_cast<size_t>(got));
28✔
202
    }
28✔
203
  }
55✔
204

205
  if (eolPos != received.size() - 1) {
37✔
206
    /* we have some data remaining after the first '\n', let's keep it for later */
207
    d_remaining = std::string(received, eolPos + 1, received.size() - eolPos - 1);
10✔
208
  }
10✔
209

210
  received.resize(eolPos);
37✔
211
  boost::trim_right(received);
37✔
212
}
37✔
213

214
void CoProcess::sendReceive(const string& snd, string& rcv)
215
{
×
216
  checkStatus();
×
217
  send(snd);
×
218
  receive(rcv);
×
219
}
×
220

221
UnixRemote::UnixRemote(const string& path)
222
{
×
223
  d_fd = socket(AF_UNIX, SOCK_STREAM, 0);
×
224
  if (d_fd < 0)
×
225
    throw PDNSException("Unable to create UNIX domain socket: " + string(strerror(errno)));
×
226

227
  struct sockaddr_un remote;
×
228
  if (makeUNsockaddr(path, &remote))
×
229
    throw PDNSException("Unable to create UNIX domain socket: Path '" + path + "' is not a valid UNIX socket path.");
×
230

231
  // fcntl(fd, F_SETFL, O_NONBLOCK, &sock);
232

233
  if (connect(d_fd, (struct sockaddr*)&remote, sizeof(remote)) < 0)
×
234
    unixDie("Unable to connect to remote '" + path + "' using UNIX domain socket");
×
235

236
  d_fp = pdns::UniqueFilePtr(fdopen(d_fd, "r"));
×
237
}
×
238

239
void UnixRemote::send(const string& line)
240
{
×
241
  string nline(line);
×
242
  nline.append(1, '\n');
×
243
  writen2(d_fd, nline);
×
244
}
×
245

246
void UnixRemote::receive(string& line)
247
{
×
248
  line.clear();
×
249
  stringfgets(d_fp.get(), line);
×
250
  boost::trim_right(line);
×
251
}
×
252

253
void UnixRemote::sendReceive(const string& snd, string& rcv)
254
{
×
255
  //  checkStatus();
256
  send(snd);
×
257
  receive(rcv);
×
258
}
×
259

260
bool isUnixSocket(const string& fname)
261
{
10✔
262
  struct stat st;
10✔
263
  if (stat(fname.c_str(), &st) < 0)
10!
264
    return false; // not a unix socket in any case ;-)
×
265

266
  return (st.st_mode & S_IFSOCK) == S_IFSOCK;
10✔
267
}
10✔
268

269
#ifdef TESTDRIVER
270
main()
271
{
272
  try {
273
    CoProcess cp("./irc.pl");
274
    string reply;
275
    cp.sendReceive("www.trilab.com", reply);
276
    cout << "Answered: '" << reply << "'" << endl;
277
  }
278
  catch (PDNSException& ae) {
279
    cerr << ae.reason << endl;
280
  }
281
}
282
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc