add session handling
[libi2ncommon] / src / daemonfunc.cpp
index 99f3be8..6481493 100644 (file)
@@ -25,6 +25,8 @@ on this file might be covered by the GNU General Public License.
 #include <stdlib.h>
 #include <pwd.h>
 #include <grp.h>
+#include <errno.h>
+#include <string.h>
 
 #include <string>
 #include <stdexcept>
@@ -102,6 +104,40 @@ bool drop_root_privileges(const std::string &username,
     return true;
 }
 
+static const char *const EXE_DELETED_MARKER = " (deleted)";
+
+/** @brief      Remove the deletion marker from a binary name, if applicable.
+ *
+ * @param exe   Binary name as obtained from ``/proc/$PID/exe``.
+ *
+ * @return      The input binary name with a trailing `` (deleted)`` removed.
+ *
+ * From proc(5):
+ *
+ *     /proc/[pid]/exe
+ *            […] If the pathname has been unlinked, the symbolic link will
+ *            contain the string '(deleted)' appended to the original pathname.
+ *
+ * Which introduces an ambiguity if the path of the actual binary ends in
+ * `` (deleted)``.
+ */
+static inline std::string
+strip_deleted_marker (const std::string &exe)
+{
+    if (exe.size () <= sizeof (*EXE_DELETED_MARKER)) {
+        /* This binary name can’t possibly contain a deleted marker. */
+        return exe;
+    }
+
+    const size_t found = exe.rfind (EXE_DELETED_MARKER);
+    if (found == std::string::npos) {
+        /* Input does not contain the deleted marker. */
+        return exe;
+    }
+
+    return exe.substr (0, found);
+}
+
 /**
  * @brief determine the pids for a given program
  * @param[in] name name (or full path) of the binary
@@ -139,21 +175,22 @@ bool pid_of(const std::string& name, std::vector< pid_t >& result)
       if (!real_exe.empty())
       {
          // we got the path of the exe
-         if (real_exe == name)
+         if (real_exe == name || strip_deleted_marker (real_exe) == name)
          {
-               result.push_back( pid );
-               continue;
+                 result.push_back( pid );
+                 continue;
          }
-
+         
          std::string proc_stat= read_file( base_path + "/stat");
          if (proc_stat.empty()) continue; // process vanished
-
+         
          //TODO some more fuzz tests here?! (cmdline, stat(us))
-
-         if (basename(real_exe) == name)
+         
+         real_exe = basename (real_exe);
+         if (real_exe == name || strip_deleted_marker (real_exe) == name)
          {
-               fuzz2_result.push_back(pid);
-               continue;
+                 fuzz2_result.push_back(pid);
+                 continue;
          }
       }
       else
@@ -192,5 +229,30 @@ bool pid_of(const std::string& name, std::vector< pid_t >& result)
    return true;
 } // eo pidOf(const std::string&,std::vector< pid_t >&)
 
+/**
+ * @brief   establish a new session for the current process.
+ *
+ * @return  the id of the new session.
+ *
+ * This wraps setsid(2); to be called from a child process after forking.
+ * Raises ``runtime_error`` if the call fails.
+ */
+pid_t create_session (void)
+{
+    pid_t sid;
+
+    errno = 0;
+    if ((sid = setsid ()) == -1)
+    {
+        throw std::runtime_error
+            ((std::string)
+             "create_session: setsid() returned an error ("
+             + I2n::to_string (errno)
+             + "): " + strerror (errno));
+    }
+
+    return sid;
+}
+
 }
 }