From c8d6d67be719368ddcd4c9e94866d4a42fabb952 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sat, 24 May 2014 14:02:32 +0100 Subject: [PATCH] service.py: Fix the RemoteService `_mkcmd' protocol. Organization: Straylight/Edgeware From: Mark Wooding The CommandRemoteService implementation was completely wrong. Substitutions need know the user name in question, which isn't available until the actual operation is invoked; so this needs to be passed through to `_mkcmd'. Therefore, we introduce a new argument to `_run', which is simply passed on to `_mkcmd', and adjust implementations to cope. --- service.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/service.py b/service.py index c04e99e..9ba9fca 100644 --- a/service.py +++ b/service.py @@ -183,17 +183,17 @@ class BasicRemoteService (BasicService): configuration. """ - def _run(me, cmd, input = None): + def _run(me, cmd, input = None, state = None): """ This is the core of the remote service machinery. It issues a command and parses the response. It will generate strings of informational output from the command; error responses cause appropriate exceptions to be raised. - The command is determined by passing the CMD argument to the `_mkcmd' - method, which a subclass must implement; it should return a list of - command-line arguments suitable for `subprocess.Popen'. The INPUT is a - string to make available on the command's stdin; if None, then no input + The command is determined by passing the CMD and STATE arguments to the + `_mkcmd' method, which a subclass must implement; it should return a list + of command-line arguments suitable for `subprocess.Popen'. The INPUT is + a string to make available on the command's stdin; if None, then no input is provided to the command. The `_describe' method must provide a description of the remote service for use in timeout messages. @@ -207,7 +207,7 @@ class BasicRemoteService (BasicService): ## Run the command and collect its output and status. with U.timeout(30, "waiting for remote service %s" % me._describe()): - proc = SUB.Popen(me._mkcmd(cmd), + proc = SUB.Popen(me._mkcmd(cmd, state), stdin = input is not None and SUB.PIPE or None, stdout = SUB.PIPE, stderr = SUB.PIPE) out, err = proc.communicate(input) @@ -248,9 +248,9 @@ class BasicRemoteService (BasicService): if not win: raise U.ExpectedError, (500, 'No reply from remote service') - def _run_noout(me, cmd, input = None): + def _run_noout(me, cmd, input = None, state = None): """Like `_run', but expect no output.""" - for _ in me._run(cmd, input): + for _ in me._run(cmd, input, state): raise U.ExpectedError, (500, 'Unexpected output from remote service') class SSHRemoteService (BasicRemoteService): @@ -332,27 +332,27 @@ class CommandRemoteService (BasicRemoteService): super(CommandRemoteService, me).__init__(*args, **kw) me._set = set me._clear = clear - me._map = dict(u = user) def _describe(me): """Description of the remote service.""" return "`%s' command service (%s)" % (me.name, ' '.join(me._default)) - def _subst(me, c): + def _subst(me, c, map): """Return the substitution for the placeholder `%C'.""" - return me._map.get(c, c) + return map.get(c, c) - def _mkcmd(me, cmd): + def _mkcmd(me, cmd, map): """Construct the command to be executed, by substituting placeholders.""" - return [me.R_PAT.sub(lambda m: me._subst(m.group(1))) for arg in cmd] + return [me.R_PAT.sub(lambda m: me._subst(m.group(1), map), arg) + for arg in cmd] def setpasswd(me, user, passwd): """Service protocol: set the USER's password to PASSWD.""" - me._run_noout(me._set, passwd + '\n') + me._run_noout(me._set, passwd + '\n', state = dict(u = user)) def clearpasswd(me, user): """Service protocol: clear the USER's password.""" - me._run_noout(me._clear) + me._run_noout(me._clear, state = dict(u = user)) CONF.export('CommandRemoteService') -- [mdw]