# HG changeset patch # User mpm@selenic.com # Date 1119955113 28800 # Node ID 42a660abaf754c91b70d2e40e955c0193c4cff93 # Parent dd8b19114fe7b96a6f2a98cabdfddf0d61e7a8c9 [PATCH] Harden os.system -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 [PATCH] Harden os.system From: Bryan O'Sullivan Add util.system function. This is similar to os.system, but will either succeed (if the process finishes with a zero exit code) or raise a util.CommandError (if the process exits uncleanly or is killed by a signal). Add util.explain_exit function. This tends to be ubiquitous in code that calls other processes, and must describe what has gone wrong. Change some uses of os.system over to util.system. manifest hash: e3bf4adcac5b915432ec0af00efdbcef86bea4b1 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFCwSipywK+sNU5EO8RAr0RAJkBDt8XQ7mYQAWNHNgTOVt1eyWU1QCfe1oO 2OwxyWqpbRNACVJHHfZ3/Xw= =OaRX -----END PGP SIGNATURE----- diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -291,7 +291,7 @@ def clone(ui, source, dest = None, **opt if link: ui.debug("copying by hardlink\n") - os.system("cp -al %s/.hg .hg" % source) + util.system("cp -al %s/.hg .hg" % source) try: os.remove(".hg/dirstate") except: pass @@ -871,6 +871,8 @@ def dispatch(args): return r else: return d() + except util.CommandError, inst: + u.warn("abort: %s\n" % inst.args) except hg.RepoError, inst: u.warn("abort: ", inst, "!\n") except SignalInterrupt: diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -5,7 +5,7 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os, sys, re, ConfigParser +import os, sys, re, ConfigParser, util class ui: def __init__(self, verbose=False, debug=False, quiet=False, @@ -78,10 +78,7 @@ class ui: f.close() editor = os.environ.get("HGEDITOR") or os.environ.get("EDITOR", "vi") - r = os.system("%s %s" % (editor, name)) - - if r: - raise "Edit failed!" + util.system("%s %s" % (editor, name), errprefix = "edit failed") t = open(name).read() t = re.sub("(?m)^HG:.*\n", "", t) diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -7,6 +7,29 @@ import os +class CommandError(Exception): pass + +def explain_exit(code): + """return a 2-tuple (desc, code) describing a process's status""" + if os.WIFEXITED(code): + val = os.WEXITSTATUS(code) + return "exited with status %d" % val, val + elif os.WIFSIGNALED(code): + val = os.WTERMSIG(code) + return "killed by signal %d" % val, val + elif os.WIFSTOPPED(code): + val = os.STOPSIG(code) + return "stopped by signal %d" % val, val + raise ValueError("invalid exit code") + +def system(cmd, errprefix = "abort"): + """execute a shell command that must succeed""" + rc = os.system(cmd) + if rc: + errmsg = "%s: %s %s" % (errprefix, os.path.basename(cmd.split(None, 1)[0]), + explain_exit(rc)[0]) + raise CommandError(errmsg) + def rename(src, dst): try: os.rename(src, dst)