]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Replace use of system(3) in pdnsutil with execvp(2).
authorMiod Vallat <miod.vallat@powerdns.com>
Thu, 22 May 2025 06:53:36 +0000 (08:53 +0200)
committerMiod Vallat <miod.vallat@powerdns.com>
Wed, 28 May 2025 12:09:30 +0000 (14:09 +0200)
pdns/pdnsutil.cc

index c437c1dee1f41bff83256867cd3770436103e2cb..fbffdc9e0fe258c853389b3ee16eb1b6a30cb81f 100644 (file)
@@ -9,6 +9,8 @@
 #endif
 
 #include <fcntl.h>
+#include <signal.h>
+#include <sys/wait.h>
 
 #include "credentials.hh"
 #include "dnsseckeeper.hh"
@@ -1246,16 +1248,64 @@ private:
   bool d_colors;
 };
 
-static int spawnEditor(const std::string& editor, const std::string& tmpfile, int gotoline)
+static bool spawnEditor(const std::string& editor, const std::string& tmpfile, int gotoline, int &result)
 {
-  string cmdline;
-
-  cmdline=editor+" ";
-  if(gotoline > 0) {
-    cmdline+="+"+std::to_string(gotoline)+" ";
+  pid_t child;
+  sigset_t mask, omask;
+
+  // Ignore INT, QUIT and CHLD signals while the editor process runs
+  sigemptyset(&mask);
+  sigaddset(&mask, SIGCHLD);
+  sigaddset(&mask, SIGINT);
+  sigaddset(&mask, SIGQUIT);
+  sigprocmask(SIG_BLOCK, &mask, &omask);
+
+  switch (child = fork()) {
+  case 0:
+    {
+      std::array<const char *, 4> args;
+      size_t pos = 0;
+      std::string gotolinestr;
+      args[pos++] = editor.c_str();
+      if (gotoline > 0) {
+        // TODO: if editor is 'ed', skip this; if 'ex' or 'vi', use '-c number'
+        gotolinestr = "+" + std::to_string(gotoline);
+        args[pos++] = gotolinestr.c_str();
+      }
+      args[pos++] = tmpfile.c_str();
+      args[pos++] = nullptr;
+      if (::execvp(args.at(0), const_cast<char **>(args.data())) != 0) {
+        ::exit(errno);
+      }
+      // std::unreachable();
+    }
+    break;
+  case -1:
+    unixDie("Couldn't fork");
+    break;
+  default:
+    {
+      pid_t pid;
+      int status;
+      do {
+        pid = waitpid(child, &status, 0);
+      } while (pid == -1 && errno == EINTR);
+      sigprocmask(SIG_SETMASK, &omask, NULL);
+      if (pid == -1) {
+        return false;
+      }
+      if (WIFEXITED(status)) {
+        result = WEXITSTATUS(status);
+        return true;
+      }
+      if (WIFSIGNALED(status)) {
+        result = 128 + WTERMSIG(status);
+        return true;
+      }
+    }
+    break;
   }
-  cmdline += tmpfile;
-  return system(cmdline.c_str());
+  return false;
 }
 
 static int editZone(const ZoneName &zone, const PDNSColors& col) {
@@ -1349,9 +1399,13 @@ static int editZone(const ZoneName &zone, const PDNSColors& col) {
   }
  editMore:;
   post.clear();
-  if (spawnEditor(editor, tmpnam, gotoline) != 0) {
+  int result{0};
+  if (!spawnEditor(editor, tmpnam, gotoline, result)) {
     unixDie("Editing file with: '"+editor+"', perhaps set EDITOR variable");
   }
+  if (result != 0) {
+    throw std::runtime_error("Editing file with: '" + editor + "' returned non-zero status " + std::to_string(result));
+  }
   ZoneParserTNG zpt(static_cast<const char *>(tmpnam), g_rootzonename);
   zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
   zpt.setMaxIncludes(::arg().asNum("max-include-depth"));