Python: タイムアウト付きでコマンド実行

にわか Python 野郎です。 Python の勉強のネタに、 ミラクル・リナックスの某ブログで話題になったコマンドのタイムアウト処理 を timeoutcmd.py として実装してみた。 ちなみに、これが初のスタンドアロンな Python スクリプト。 2007-01-01 更新: タイムアウト発生時にコマンドに送るシグナルを指定できるようにした。

#!/usr/bin/env python

#

Run a command with a timeout checking

Copyright (c) 2006-2007 SATOH Fumiyasu @ OSS Technology Co.

http://www.osstech.co.jp/

#

Date: 2007-01-01, since 2006-12-31

License: GNU General Public License version 2

import getopt import signal import re import sys import os import select def err(format, *argv): print >>sys.stderr, sys.argv[0] + ":", format % argv signum_by_name = {} signame_by_num = {} for signame in dir(signal): if signame.startswith("SIG"): signum = getattr(signal, signame) if type(signum) == type(1): signum_by_name[str(signum)] = signum signum_by_name[signame] = signum signum_by_name[re.sub("^SIG", "", signame)] = signum signame_by_num[signum] = signame

Options

======================================================================

codeontimeout = 255 codeonexecerror = 254 codeonforkerror = 253 signumontimeout = signal.SIGTERM usage = """Usage: %s [OPTIONS] TIMEOUT COMMAND [ARG ...] Options: -s, --signal SIGNAL Send SIGNAL to COMMAND on timeout -o, --timeout-code=EXITCODE Exit code on timeout -e, --exec-error-code=EXITCODE Exit code on exec failure -f, --fork-error-code=EXITCODE Exit code on fork failure """

Command-line options

----------------------------------------------------------------------

try: opts, args = getopt.getopt(sys.argv[1:], 'hs:o:e:f:', ['help', 'signal=', 'timeout-code=', 'exec-error-code=', 'fork-error-code=']) except getopt.error, msg: err("%s", msg) sys.exit(1) for opt, arg in opts: if opt in ('-h', '--help'): print usage sys.exit(0) if opt in ('-s', '--signal'): signame = arg.upper() if not signumbyname.haskey(signame): err("invalid signal name: %s", signame) sys.exit(1) signumontimeout = signumbyname[signame] if opt in ('-o', '--timeout-code'): codeontimeout = int(arg) if opt in ('-e', '--exec-error-code'): codeonexecerror = int(arg) if opt in ('-f', '--fork-error-code'): codeonfork_error = int(arg) timeout = float(args[0]) command = args[1:]

Main

======================================================================

r, w = os.pipe() try: pid = os.fork() except OSError, e: err("fork failed: %s", e.strerror) sys.exit(codeonfork_error)

Child process

----------------------------------------------------------------------

if pid == 0: os.close(r) try: os.execvp(command[0], command) except OSError, e: err("exec failed: %s: %s", command[0], e.strerror) sys.exit(codeonexec_error)

Parent process

----------------------------------------------------------------------

os.close(w) p = select.poll() p.register(r, select.POLLIN) ready = p.poll(timeout * 1000) if len(ready) == 0: err("command timed out: %s", command[0]) os.kill(pid, signumontimeout) pidexited, status = os.waitpid(pid, 0) sys.exit(codeontimeout) pidexited, status = os.waitpid(pid, 0) sys.exit(status >> 8)

Mailman のハンドラを書いていて、 自然にオブジェクト指向 & 例外処理を書けるのは気持ちいい! と感じて Python を覚えようかと思ってみたものの、 例えばリストの長さを求めるのに len(list) と書くのは嫌いかも。 同様に int(string) とか str(number) も嫌だなぁ。 Ruby みたいに list.length のように書ける ほうが好みだな。

Python は Mailman のハンドラ書きとデバッグ程度の嗜みにしておいて、 ポスト Perl は Ruby にしてみようかしら?

add to hatena hatena.comment (1) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 1

コメントを書く

Your email is never shared. * 印の項目は必須項目です。

*
*