Tim Hammerquist | 545ba4e | 2017-12-12 02:36:17 +0000 | [diff] [blame] | 1 | # COPYRIGHT LINE: FIXME |
| 2 | |
| 3 | """ |
| 4 | dbsign.shell |
| 5 | |
| 6 | shell routines for debugsign |
| 7 | """ |
| 8 | |
| 9 | from __future__ import print_function |
| 10 | |
| 11 | import os |
| 12 | from subprocess import PIPE, Popen |
| 13 | |
| 14 | import dbsign.logger |
| 15 | |
| 16 | |
| 17 | log = dbsign.logger.get_logger(__name__) |
| 18 | |
| 19 | |
| 20 | class ShellCommand(object): |
| 21 | """ |
| 22 | Represents the result of a shell command |
| 23 | """ |
| 24 | def __init__(self, args, code, stdout, stderr): |
| 25 | # type: (list[str], int, str, str) -> () |
| 26 | self.data = { |
| 27 | 'args': args, |
| 28 | 'code': code, |
| 29 | 'stdout': stdout, |
| 30 | 'stderr': stderr, |
| 31 | } |
| 32 | |
| 33 | def __eq__(self, rhs): # type: (ShellCommand) -> bool |
| 34 | return self.data == rhs.data |
| 35 | |
| 36 | def __getattr__(self, attr): # type: (str) -> T |
| 37 | if attr in self.data: |
| 38 | return self.data[attr] |
| 39 | raise AttributeError(attr) |
| 40 | |
| 41 | def __nonzero__(self): # type: () -> bool |
| 42 | return self.code == 0 |
| 43 | |
| 44 | def __repr__(self): # type: () -> str |
| 45 | repr_fmt = "{0}(args={1.args!r}, code={1.code!r}," |
| 46 | repr_fmt += " stdout={1.stdout!r}, stderr={1.stderr!r})" |
| 47 | return repr_fmt.format(self.__class__.__name__, self) |
| 48 | |
| 49 | |
| 50 | def __run(args, stdin=None): # type: (list[str], str) -> ShellCommand |
| 51 | """internal function to run shell commands""" |
| 52 | try: |
| 53 | p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) |
| 54 | stdout, stderr = p.communicate(input=stdin) |
| 55 | except OSError as os_err: |
| 56 | log.debug('Unable to execute command: %s: %s', args, os_err) |
| 57 | raise |
| 58 | |
| 59 | cmd = ShellCommand(args, code=p.returncode, stdout=stdout, stderr=stderr) |
| 60 | log.debug(cmd) |
| 61 | return cmd |
| 62 | |
| 63 | |
| 64 | def run(args, stdin=None): # type: (list[str]) -> ShellCommand |
| 65 | """Run a regular (non-sudo) command""" |
| 66 | log.debug("run(args=%s)", repr(args)) |
| 67 | |
| 68 | if os.path.basename(args[0]).startswith('su'): |
| 69 | log.info('run() called with illegal command `%s`', args) |
| 70 | raise RuntimeError('Unauthorized use of run; use sudo_run') |
| 71 | |
| 72 | return __run(args, stdin) |
| 73 | |
| 74 | |
| 75 | def sudo_run(args, stdin=None): # type: (list[str]) -> ShellCommand |
| 76 | """Run a command with root privileges using sudo""" |
| 77 | log.debug("sudo_run(args=%s)", repr(args)) |
| 78 | |
| 79 | args.insert(0, 'sudo') |
| 80 | |
| 81 | return __run(args, stdin) |