import argparse
import subprocess
import shlex
import signal
# Function to execute a binary and kill it when a needle is found in its output or after a timeout
# Parameters:
# binary_path: Path to the binary to execute
# binary_parameters: Parameters to pass to the binary
# needle: Needle to match in the output
# timeout: Timeout after which to kill the process
# Returns:
# The return code of the binary
# Notes:
# This function uses subprocess.Popen to execute the binary in a sandbox and capture its output.
# It then loops through the output lines and checks if the needle is found in the output.
# If the needle is found, the process is killed and the function returns. If the needle is not found,
# the function waits for the process to finish and returns its return code.
# Since popen is used, it does not accept a timeout parameter.
# Instead, the timeout is implemented by using the timeout command to run the binary.
# We could not use the timeout parameter of subprocess.run because it blocks the execution of the script
# until the process finishes.
# This means that we would not be able to capture the output of the process.
def using_popen(binary_path, binary_parameters, needle, timeout):
# Define the command to run the binary file
command = f"timeout {timeout} {binary_path} {binary_parameters}"
# Use subprocess to execute the command in a sandbox and capture its output
process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Loop through the output lines and process them
while True:
output = process.stderr.readline().decode().strip()
if output == "" and process.poll() is not None:
break
if output != "":
# Process the output here
print(output)
# Check if the output contains the keyword
if needle in output:
# Kill the process if the keyword is found
process.send_signal(signal.SIGINT)
break
return process.returncode
if __name__ == "__main__":
# Parse command line arguments
parser = argparse.ArgumentParser(
description='Execute a binary and kill it either when a needle is found in its output or after a timeout')
parser.add_argument('-e', '--executable', type=str, help='Path to the executable to run')
parser.add_argument('-p', '--parameters', type=str, help='Parameters to pass to the executable')
parser.add_argument('-n', '--needle', type=str, help='Needle to match in the output')
parser.add_argument('-t', '--timeout', type=str, help='Timeout after which to kill the process')
# Example usage
# python main.py -e "python" -p "-u -m http.server" -n "404" -t "15s"
args = parser.parse_args()
# Execute the binary and capture its output
return_code = using_popen(args.executable, args.parameters, args.needle, args.timeout)
# Print the http server's return code
print("Http server returned with code:", return_code)
The given code is a Python script that executes a binary and kills it when a specified string is found in its output or after a specified timeout. Let’s break down the code into its constituent parts.
First, the script imports several Python modules – argparse, subprocess, shlex, and signal.
argparse
is a module that makes it easy to write user-friendly command-line interfaces. It parses the command-line arguments and options specified by the user and returns them in a convenient format.
subprocess
is a module that allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes.
shlex
is a module that provides a simple way to parse command-line strings into a list of tokens, which can then be passed to subprocess.Popen()
.
signal
is a module that provides mechanisms to handle asynchronous events such as interrupts and signals.
Next, the script defines a function called using_popen()
that takes four parameters: binary_path
, binary_parameters
, needle
, and timeout
. The function first constructs a command string that combines the binary_path
and binary_parameters
arguments and adds a timeout
command to limit the execution time of the process.
The subprocess.Popen()
function is then called to create a new process with the constructed command. The stdout
and stderr
arguments are set to subprocess.PIPE
so that the output of the process can be captured.
A while loop is then entered to read the process’s stderr
output line by line. The decode()
method is used to convert the byte string output to a regular string, and the strip()
method is used to remove any whitespace characters from the beginning and end of the string.
If the output string is empty and the process has finished running (process.poll() is not None
), the loop is terminated. Otherwise, if the output string is not empty, it is printed to the console.
If the specified needle
string is found in the output, the process is terminated by sending a signal.SIGINT
signal to it.
After the loop has completed, the return code of the process is retrieved using process.returncode
and returned by the function.
Finally, the script checks if it is being run as the main program using the __name__
attribute. If it is, it uses the argparse
module to parse the command-line arguments specified by the user. The using_popen()
function is then called with the parsed arguments, and its return code is printed to the console along with a message indicating the completion of the script.
In summary, this script provides a convenient way to execute a binary with specified parameters, limit its execution time, and terminate it early if a specified string is found in its output. It also provides a user-friendly command-line interface for specifying these parameters.