aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>2016-03-21 14:43:22 +0200
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>2016-03-21 17:10:54 +0200
commitef398438aec0692bf98e2e6e75194f18beae3169 (patch)
treea326a2f678b8b001eb702df095900c15d59ed8df
parent9c200760cbbe322ed884729eb395f389c863e1c8 (diff)
downloadopenembedded-core-contrib-ef398438aec0692bf98e2e6e75194f18beae3169.tar.gz
oe/gpg_sign: use our own immplementation of pexpect
Implement a simple python-expect replacement in order to get rid of the (currently undocumented) external dependency. Pexpect is only used for rpm package signing. [YOCTO #9304] Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
-rw-r--r--meta/lib/oe/gpg_sign.py104
1 files changed, 90 insertions, 14 deletions
diff --git a/meta/lib/oe/gpg_sign.py b/meta/lib/oe/gpg_sign.py
index e738397880..9887c1d867 100644
--- a/meta/lib/oe/gpg_sign.py
+++ b/meta/lib/oe/gpg_sign.py
@@ -1,9 +1,87 @@
"""Helper module for GPG signing"""
import os
+import time
import bb
import oe.utils
+
+class Pexpect(object):
+ """Naive and limited (p)expect functionality"""
+ class PexpectErr(Exception):
+ """Pexpect error"""
+ pass
+
+ def __init__(self, cmd):
+ import pty
+ self.pid, self.fd = pty.fork()
+ if self.pid == 0:
+ os.execv(cmd[0], cmd)
+ self.status = None
+ self.buf = ''
+ self.buf_readp = 0
+
+ def check_exitstatus(self):
+ """Return child status or None if still alive"""
+ if self.status is None:
+ pid, status = os.waitpid(self.pid, os.WNOHANG)
+ if pid != 0:
+ self.status = status
+ return self.status
+
+ def close(self):
+ """Close connection and terminate our child"""
+ import signal
+ if self.fd == -1:
+ return
+ os.close(self.fd)
+ self.fd = -1
+ time.sleep(0.1)
+
+ # Kill child process if it's still alive
+ if self.check_exitstatus() is None:
+ os.kill(self.pid, signal.SIGHUP)
+ # Give the process some time to terminate peacefully
+ time.sleep(0.5)
+ if self.check_exitstatus() is None:
+ os.kill(self.pid, signal.SIGKILL)
+ time.sleep(0.5)
+ if self.check_exitstatus() is None:
+ bb.warn('Failed to kill PID %d' % self.pid)
+
+ def expect_exact(self, expected, timeout):
+ """Wait until expected output is detected. Use None to wait until EOF"""
+ import errno
+ import select
+ end_time = time.time() + timeout
+ while time.time() < end_time:
+ ready = select.select([self.fd], [], [], end_time - time.time())
+ if ready[0]:
+ try:
+ self.buf += os.read(self.fd, 4096)
+ except OSError as err:
+ if err.errno == errno.EIO:
+ if expected is None:
+ return
+ else:
+ raise self.PexpectErr("Unexpected EOF")
+ raise
+ if expected is not None:
+ ind = self.buf.find(expected, self.buf_readp)
+ if ind >= 0:
+ self.buf_readp = ind + len(expected)
+ return
+ elif len(self.buf) > len(expected):
+ # No need to search from beginning of buf every time
+ self.buf_readp = len(self.buf) - len(expected)
+ raise self.PexpectErr("Timeout")
+
+ def sendline(self, data):
+ """Write data to child proces stdin"""
+ os.write(self.fd, data)
+ os.write(self.fd, '\n')
+
+
class LocalSigner(object):
"""Class for handling local (on the build host) signing"""
def __init__(self, d):
@@ -28,28 +106,26 @@ class LocalSigner(object):
def sign_rpms(self, files, keyid, passphrase_file):
"""Sign RPM files"""
- import pexpect
-
- cmd = self.rpm_bin + " --addsign --define '_gpg_name %s' " % keyid
+ cmd = [self.rpm_bin, '--addsign', '--define', '_gpg_name ' + keyid]
if self.gpg_bin:
- cmd += "--define '%%__gpg %s' " % self.gpg_bin
+ cmd += ['--define', '__gpg ' + self.gpg_bin]
if self.gpg_path:
- cmd += "--define '_gpg_path %s' " % self.gpg_path
- cmd += ' '.join(files)
+ cmd += ['--define', '_gpg_path ' + self.gpg_path]
+ cmd += files
# Need to use pexpect for feeding the passphrase
- proc = pexpect.spawn(cmd)
+ proc = Pexpect(cmd)
try:
proc.expect_exact('Enter pass phrase:', timeout=15)
with open(passphrase_file) as fobj:
proc.sendline(fobj.readline().rstrip('\n'))
- proc.expect(pexpect.EOF, timeout=900)
- proc.close()
- except pexpect.TIMEOUT as err:
- bb.error('rpmsign timeout: %s' % err)
- proc.terminate()
- if os.WEXITSTATUS(proc.status) or not os.WIFEXITED(proc.status):
- bb.error('rpmsign failed: %s' % proc.before.strip())
+ proc.expect_exact(None, timeout=900)
+ except Pexpect.PexpectErr as err:
+ bb.error('rpmsign unexpected output: %s' % err)
+ proc.close()
+ status = proc.check_exitstatus()
+ if os.WEXITSTATUS(status) or not os.WIFEXITED(status):
+ bb.error('rpmsign failed: %s' % proc.buf[proc.buf_readp:])
raise bb.build.FuncFailed("Failed to sign RPM packages")