chiark / gitweb /
service.py: Fix the RemoteService `_mkcmd' protocol.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 24 May 2014 13:02:32 +0000 (14:02 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 24 May 2014 21:58:50 +0000 (22:58 +0100)
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

index c04e99e..9ba9fca 100644 (file)
@@ -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
+    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')