1From 8a18a6c1dea7cce6669d0eeb4230e85aa88d8e44 Mon Sep 17 00:00:00 2001
2From: Hongxu Jia <hongxu.jia@windriver.com>
3Date: Fri, 23 Nov 2018 17:03:58 +0800
4Subject: [PATCH 02/11] run_program support timeout
5
6Upstream-Status: Pending
7
8Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>
9
10Rebase for blivet 3.12.1.
11
12Signed-off-by: Kai Kang <kai.kang@windriver.com>
13---
14 blivet/util.py | 70 ++++++++++++++++++++++++++++++++++------------------------
15 1 file changed, 41 insertions(+), 29 deletions(-)
16
17diff --git a/blivet/util.py b/blivet/util.py
18index d194a5b..5985164 100644
19--- a/blivet/util.py
20+++ b/blivet/util.py
21@@ -169,6 +169,30 @@ class Path(str):
22     def __hash__(self):
23         return self._path.__hash__()
24
25+def timeout_command(argv, timeout, *args, **kwargs):
26+    """call shell-command and either return its output or kill it
27+    if it doesn't normally exit within timeout seconds and return None"""
28+    import subprocess, datetime, os, time, signal
29+    start = datetime.datetime.now()
30+
31+    try:
32+        proc = subprocess.Popen(argv, *args, **kwargs)
33+        while proc.poll() is None:
34+            time.sleep(0.1)
35+            now = datetime.datetime.now()
36+            if (now - start).seconds> timeout:
37+                os.kill(proc.pid, signal.SIGKILL)
38+                os.waitpid(-1, os.WNOHANG)
39+                program_log.debug("%d seconds timeout" % timeout)
40+                return (-1, None)
41+
42+
43+    except OSError as e:
44+        program_log.error("Error running %s: %s", argv[0], e.strerror)
45+        raise
46+
47+    program_log.debug("Return code: %d", proc.returncode)
48+    return (proc.returncode, proc.stdout.read())
49
50 def _run_program(argv, root='/', stdin=None, env_prune=None, stderr_to_stdout=False, binary_output=False):
51     if env_prune is None:
52@@ -191,35 +215,23 @@ def _run_program(argv, root='/', stdin=None, env_prune=None, stderr_to_stdout=Fa
53             stderr_dir = subprocess.STDOUT
54         else:
55             stderr_dir = subprocess.PIPE
56-        try:
57-            proc = subprocess.Popen(argv,  # pylint: disable=subprocess-popen-preexec-fn
58-                                    stdin=stdin,
59-                                    stdout=subprocess.PIPE,
60-                                    stderr=stderr_dir,
61-                                    close_fds=True,
62-                                    preexec_fn=chroot, cwd=root, env=env)
63-
64-            out, err = proc.communicate()
65-            if not binary_output:
66-                out = out.decode("utf-8")
67-            if out:
68-                if not stderr_to_stdout:
69-                    program_log.info("stdout:")
70-                for line in out.splitlines():
71-                    program_log.info("%s", line)
72-
73-            if not stderr_to_stdout and err:
74-                program_log.info("stderr:")
75-                for line in err.splitlines():
76-                    program_log.info("%s", line)
77-
78-        except OSError as e:
79-            program_log.error("Error running %s: %s", argv[0], e.strerror)
80-            raise
81-
82-        program_log.debug("Return code: %d", proc.returncode)
83-
84-    return (proc.returncode, out)
85+
86+        res, out = timeout_command(argv, 10,
87+                                   stdin=stdin,
88+                                   stdout=subprocess.PIPE,
89+                                   stderr=stderr_dir,
90+                                   close_fds=True,
91+                                   preexec_fn=chroot, cwd=root, env=env)
92+
93+        if not binary_output:
94+            out = out.decode("utf-8")
95+        if out:
96+            if not stderr_to_stdout:
97+                program_log.info("stdout:")
98+            for line in out.splitlines():
99+                program_log.info("%s", line)
100+
101+    return (res, out)
102
103
104 def run_program(*args, **kwargs):
105