`clear SERVICE USER'
Clear the USER's password for SERVICE.
+ `mkpwent USER SERVICE [FIELDS ...]'
+ Install a record for USER in the SERVICE, supplying any other
+ necessary FIELDS in the appropriate format. The user's password is
+ provided on the next line of standard input.
+
+ `rmpwent USER SERVICE'
+ Remove USER's password record for SERVICE.
+
Arguments are form-url-encoded, since SSH doesn't preserve token boundaries
in its argument list.
containing `%' placeholders, as follows:
`%u' the user's name
+ `%f' a user record field (list-valued)
`%%' a single `%' character
+ If a template word contains placeholders for list-valued arguments, then
+ one output word is produced for each element of each list, with the
+ rightmost placeholder varying fastest. If any list is empty then no output
+ words are produced.
+
On success, the commands should print a line `OK' to standard output, and
on any kind of anticipated failure, they should print `ERR' followed by an
HTTP status code and a message; in either case, the program should exit
commands, then your easy approach is to set commands for the operations you
can handle in the OPMAP, and set the DEFAULT to something like
- ['echo', 'ERR 500', 'unsupported command:']
+ ['echo', 'ERR', '500', 'unsupported command:']
to reject other commands.
"""
R_PAT = RX.compile('%(.)')
- def __init__(me, default = ['ERR', '500', 'unimplemented command:'],
+ def __init__(me,
+ default = ['echo', 'ERR', '500', 'unimplemented command:'],
opmap = {}, *args, **kw):
"""Initialize the command remote service."""
super(CommandRemoteService, me).__init__(*args, **kw)
"""Description of the remote service."""
return "`%s' command service (%s)" % (me.name, ' '.join(me._default))
- def _subst(me, c, map):
- """Return the substitution for the placeholder `%C'."""
- return map.get(c, c)
+ def _mkcmd(me, cmd, argmap):
+ """
+ Construct the command to be executed, by substituting placeholders.
+
+ The ARGMAP is a dictionary mapping placeholder letters to lists of
+ arguments. These are substituted cartesian-product style into the
+ command words.
+ """
- def _mkcmd(me, cmd, map):
- """Construct the command to be executed, by substituting placeholders."""
- if map is None: return cmd
- return [me.R_PAT.sub(lambda m: me._subst(m.group(1), map), arg)
- for arg in cmd]
+ ## No command map, so assume someone's already done the hard word.
+ if argmap is None: return cmd
+
+ ## Start on building a list of arguments.
+ ww = []
+
+ ## Work through each template argument in turn...
+ for w in cmd:
+
+ ## Firstly, build a list of lists. We'll then take the cartesian
+ ## product of these, and concatenate each of the results.
+ pc = []
+ last = 0
+ for m in me.R_PAT.finditer(w):
+ start, end = m.start(0), m.end(0)
+ if start > last: pc.append([w[last:start]])
+ ch = m.group(1)
+ if ch == '%':
+ pc.append(['%'])
+ else:
+ try: pc.append(argmap[m.group(1)])
+ except KeyError: raise U.ExpectedError, (
+ 500, "Unknown placeholder `%%%s' in command `%s'" % (ch, cmd))
+ last = end
+ if last < len(w): pc.append([w[last:]])
+
+ ## If any of the components is empty then there's nothing to do for
+ ## this word.
+ if not all(pc): continue
+
+ ## Now do all the substitutions.
+ ii = len(pc)*[0]
+ while True:
+ ww.append(''.join(map(lambda v, i: v[i], pc, ii)))
+ i = len(ii) - 1
+ while i >= 0:
+ ii[i] += 1
+ if ii[i] < len(pc[i]): break
+ ii[i] = 0
+ i -= 1
+ else:
+ break
+
+ ## And finally we're done.
+ return ww
def _dispatch(me, func, op, args, input = None):
"""
Invoke FUNC, which works like `_run', with appropriate arguments. The OP
is a remote command name; ARGS is a sequence of (C, ARG) pairs, where C
- is a placeholder character and ARG is a string value; INPUT is the text
- to provide to the command on standard input.
+ is a placeholder character and ARG is a list of string values; INPUT is
+ the text to provide to the command on standard input.
"""
try:
cmd = me._opmap[op]
except KeyError:
- cmd = me._default + [op] + [v for k, v in args]
+ cmd = me._default + [op] + reduce(lambda x, y: x + y,
+ [v for k, v in args], [])
map = None
else:
map = dict(args)
def setpasswd(me, user, passwd):
"""Service protocol: set the USER's password to PASSWD."""
- me._dispatch(me._run_noout, 'set', [('u', user)], passwd + '\n')
+ me._dispatch(me._run_noout, 'set', [('u', [user])],
+ input = passwd + '\n')
def clearpasswd(me, user):
"""Service protocol: clear the USER's password."""
- me._dispatch(me._run_noout, 'clear', [('u', user)])
+ me._dispatch(me._run_noout, 'clear', [('u', [user])])
def mkpwent(me, user, passwd, fields):
"""Service protocol: create a record for USER."""
- me._dispatch(me._run_noout, 'mkpwent', [('u', user)])
+ me._dispatch(me._run_noout, 'mkpwent', [('u', [user]), ('f', fields)],
+ input = passwd + '\n')
def rmpwent(me, user):
"""Service protocol: delete the record for USER."""
- me._dispatch(me._run_noout, 'rmpwent', [('u', user)])
+ me._dispatch(me._run_noout, 'rmpwent', [('u', [user])])
CONF.export('CommandRemoteService')