From: Tobias Ulmer Date: Fri, 15 Feb 2019 10:27:04 +0000 (+0100) Subject: e2lib: add children_.. functions to track and terminate sub processes X-Git-Tag: e2factory-2.3.18rc1~45 X-Git-Url: https://git.e2factory.org/?a=commitdiff_plain;h=c7d6d7fd3c3f2e700dffabe324bdef2371ff5ef5;p=e2factory.git e2lib: add children_.. functions to track and terminate sub processes If a child process has an associated pseudo terminal, it's terminated via TIOCSIG ioctl, otherwise the standard kill() function is used. Allows us to interrupt root-owned children (eg e2-su, sudo, ...) Signed-off-by: Tobias Ulmer --- diff --git a/generic/e2lib.lua b/generic/e2lib.lua index 862a348..2ea35e8 100644 --- a/generic/e2lib.lua +++ b/generic/e2lib.lua @@ -243,10 +243,101 @@ function e2lib.wait(pid, wnohang) end end - if not rc then - return false, err.new("waiting for child %d failed: %s", pid, childpid) +local _children = {} + +--- Add child PID into a table to keep track of all active children of the +-- process. Also tracks the associated pty fd if there is one. +-- @param cpid Unique child PID +-- @param fd Pty file descriptor if one is associated with the child. +-- @param fd File descriptor of the PTY, optional. +-- @return nil +local function children_insert(cpid, fd) + fd = fd or -1 + assertIsNumber(cpid) + assertIsNumber(fd) + assertIsNil(_children[cpid]) + _children[cpid] = fd +end + +--- Remove active child PID from table. Returns pty file descriptor or -1. +-- @param cpid Active child PID. +-- @return Pty file descriptor or -1. +local function children_remove(cpid) + assertIsNumber(cpid) + assert(_children[cpid]) + local fd = _children[cpid] + _children[cpid] = nil + return fd +end + +--- Send SIGINT to all children, either via kill() or if they have an +-- associated pty descriptor, via TIOCSIG ioctl. +-- @param pid Optional PID to select specific child. +local function children_send_sigint(pid) + local rc, re + local SIGINT = 2 + local t = _children + + if pid then + t = {} + assert(_children[pid]) + t[pid] = _children[pid] + end + + for cpid, fd in pairs(t) do + if fd < 0 then + e2lib.logf(4, "(%d) sending SIGINT to child %d", + e2lib.getpid(), cpid) + + rc, re = e2lib.kill(cpid, SIGINT) + if not rc then + e2lib.logf(4, "(%d) sending SIGINT to child %d failed", + e2lib.getpid(), cpid) + end + else + e2lib.logf(4, "(%d) ioctl(TIOCSIG, SIGINT) to %d", + e2lib.getpid(), fd) + le2lib.ioctl_tiocsig_sigint(fd) + end + end +end + +--- Assert no children are left behind (on exit). +-- If this is successful, all registered children have been terminated and +-- wait()'ed on. +local function children_assert_empty() + for cpid, fd in pairs(_children) do + e2lib.logf(4, "children_assert_empty: child remaining cpid=%d fd=%d", + cpid, fd) + end + -- assert(next(_children) == nil, "exiting with children remaining!") + if next(_children) then + e2lib.logf(4, "children_assert_empty: exiting with children remaining!") end +end +--- Wait for process to terminate. +-- Also deletes the PID from the table of children and closes the pty descriptor +-- if the child had any. +-- @param pid Process ID, -1 to wait for any child. +-- @param wnohang True to set WNOHANG flag, pid == 0 indicates running process. +-- @return Exit status of process, signal + 128, or false on error. +-- @return Process ID of the terminated child or error object on failure. +-- @return Number of signal that killed the process, if any. +function e2lib.wait_pid_delete(pid, wnohang) + local rc, childpid, sig = e2lib.wait(pid, wnohang) + if rc then + local fdm, rc, re + fdm = children_remove(childpid) + if fdm >= 0 then + rc, re = eio.close(fdm) + if not rc then + e2lib.logf(4, + "wait_pid_delete: closing pty fd=%d of cpid=%d failed:\n%s", + fdm, childpid, re:tostring()) + end + end + end return rc, childpid, sig end