|
14 | 14 | import shlex |
15 | 15 | import re |
16 | 16 | import threading |
| 17 | +import atexit |
17 | 18 |
|
18 | 19 | # Python 2/3 compatibility for urllib and input |
19 | 20 | try: |
@@ -416,6 +417,28 @@ def append_url_to_file(url, dest_path): |
416 | 417 | pass |
417 | 418 | return True |
418 | 419 |
|
| 420 | +def terminate_process(proc, name="process", grace_seconds=10): |
| 421 | + """Attempts to gracefully stop a subprocess before forcing termination.""" |
| 422 | + if not proc or proc.poll() is not None: |
| 423 | + return |
| 424 | + log("Terminating qemu") |
| 425 | + log("Stopping {} (PID: {})".format(name, proc.pid)) |
| 426 | + try: |
| 427 | + proc.terminate() |
| 428 | + except Exception: |
| 429 | + pass |
| 430 | + |
| 431 | + deadline = time.time() + max(0, grace_seconds) |
| 432 | + while proc.poll() is None and time.time() < deadline: |
| 433 | + time.sleep(0.2) |
| 434 | + |
| 435 | + if proc.poll() is None: |
| 436 | + log("{} did not exit gracefully; killing.".format(name)) |
| 437 | + try: |
| 438 | + proc.kill() |
| 439 | + except Exception: |
| 440 | + pass |
| 441 | + |
419 | 442 | def create_sized_file(path, size_mb): |
420 | 443 | """Creates a zero-filled file of size_mb.""" |
421 | 444 | chunk_size = 1024 * 1024 # 1MB |
@@ -1122,6 +1145,11 @@ def get_releases(): |
1122 | 1145 | except OSError as e: |
1123 | 1146 | fatal("Failed to start QEMU: {}".format(e)) |
1124 | 1147 |
|
| 1148 | + if not config['detach']: |
| 1149 | + def cleanup_qemu(): |
| 1150 | + terminate_process(proc, "QEMU") |
| 1151 | + atexit.register(cleanup_qemu) |
| 1152 | + |
1125 | 1153 | def fail_with_output(reason): |
1126 | 1154 | stdout_data = proc.stdout.read() or b"" |
1127 | 1155 | stderr_data = proc.stderr.read() or b"" |
|
0 commit comments