Snippets

Peter Bui solson3

Created by Peter Bui last modified
#!/usr/bin/env python2.7

import sys
import getopt
import signal
import os
import errno
import time

SECONDS=10
VERBOSE=False

#Usage function
def usage(status=0):
	print '''Usage: timeout.py [-t SECONDS] command...

Options:

      -t SECONDS  SECONDS duration before killing command (default is 10 seconds)
      -v          Display verbose debugging output
'''.format(os.path.basename(sys.argv[0]))
	sys.exit(status)

def debug(message, *args):
	if VERBOSE:	
		print message.format(*args)

def handler(signum, frame):
	print 'Signal handler called with signal', signum
	raise IOError("Alarm has been triggered after 1 second")

#Parse command line options
try:
	opts, args = getopt.getopt(sys.argv[1:], "hvt:")
except getopt.GetoptError as e:
	print e
	usage(1)

for opt, arg in opts:
    if opt == '-v':
        VERBOSE=True
    elif opt == '-h':
	usage(1)
    elif opt == '-t':
	SECONDS=int(arg)
    else:
        usage(1)

COMMAND=" ".join(args)
debug('Executing "{}" for at most {} seconds...', COMMAND, SECONDS)

try:
	pid = os.fork()
	debug('Forking...', COMMAND, SECONDS)
except OSError as e:
	print 'Fork failed: {}'.format(e)
	sys.exit(1)

try:
	signal.signal(signal.SIGALRM, handler)
	signal.alarm(SECONDS)
	debug('Enabling Alarm...', COMMAND, SECONDS)
except OSError as e:
	print 'Alarm failed: {}'.format(e)
	sys.exit(1)

try:
	newpid, status = os.wait()
	debug('Waiting: ', COMMAND, SECONDS)
except OSError as e:
	if e.errno == errno.EINTR:
		pid, status=os.wait()
	else:
		print e
except IOError as e:
	#If alarm triggered, then kill it
	try:
		os.kill(pid, status)
		os.kill(pid, sig)
		debug('Killing PID...', COMMAND, SECONDS)
	except OSError as e:
		print 'Killed failed: {}'.format(e)
		sys.exit(1)

if pid == 0: #child
	try:
		os.execlp(COMMAND,COMMAND)
		debug('Execing...', COMMAND, SECONDS)
	except OSError as e:
		print 'Execing failed: {}'.format(e)
		sys.exit(1)
	except IOError as e:
		#If alarm triggered, then kill it
		try:
			os.kill(pid, status)
			os.kill(pid, sig)
			debug('Killing PID...', COMMAND, SECONDS)
		except OSError as e:
			print 'Killed failed: {}'.format(e)
			sys.exit(1)


try:
	signal.alarm(0) #disable alarm
	debug('Disabling Alaram...', COMMAND, SECONDS)
except OSError as e:
	print 'Alarm failed: {}'.format(e)
	sys.exit(1)
except IOError as e:
	#If alarm triggered, then kill it
	try:
		os.kill(pid, status)
		os.kill(pid, sig)
		debug('Killing PID...', COMMAND, SECONDS)
	except OSError as e:
		print 'Killed failed: {}'.format(e)
		sys.exit(1)

Comments (1)

  1. Peter Bui

    After you fork, you should do the following:

    Child

    1. Execute the command
    2. If exec fails, then exit with an error code

    Parent

    1. Register signal handler for SIGALRM
    2. Set Alarm signal
    3. Wait for child

    While waiting for the child, two things can happen:

    1. The child exits within the time frame, so the the parent will receive the pid and status and should exit with that status

    2. The child runs too long, so the alarm will interrupt the parent. When this happens, the handler should kill the child process.

    If the parent killed the child, then it must wait again for it.

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.