summaryrefslogtreecommitdiff
path: root/sys/src/cmd/python/Demo/rpc
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
committercinap_lenrek <cinap_lenrek@localhost>2011-05-03 11:25:13 +0000
commit458120dd40db6b4df55a4e96b650e16798ef06a0 (patch)
tree8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/python/Demo/rpc
parent3a742c699f6806c1145aea5149bf15de15a0afd7 (diff)
add hg and python
Diffstat (limited to 'sys/src/cmd/python/Demo/rpc')
-rw-r--r--sys/src/cmd/python/Demo/rpc/MANIFEST10
-rw-r--r--sys/src/cmd/python/Demo/rpc/README31
-rw-r--r--sys/src/cmd/python/Demo/rpc/T.py22
-rw-r--r--sys/src/cmd/python/Demo/rpc/mountclient.py202
-rw-r--r--sys/src/cmd/python/Demo/rpc/nfsclient.py201
-rw-r--r--sys/src/cmd/python/Demo/rpc/rnusersclient.py98
-rw-r--r--sys/src/cmd/python/Demo/rpc/rpc.py893
-rwxr-xr-xsys/src/cmd/python/Demo/rpc/test24
-rw-r--r--sys/src/cmd/python/Demo/rpc/xdr.py200
9 files changed, 1681 insertions, 0 deletions
diff --git a/sys/src/cmd/python/Demo/rpc/MANIFEST b/sys/src/cmd/python/Demo/rpc/MANIFEST
new file mode 100644
index 000000000..e65f3ebee
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/MANIFEST
@@ -0,0 +1,10 @@
+ File Name Archive # Description
+-----------------------------------------------------------
+ MANIFEST 1 This shipping list
+ README 1
+ T.py 1
+ mountclient.py 1
+ nfsclient.py 1
+ rpc.py 1
+ test 1
+ xdr.py 1
diff --git a/sys/src/cmd/python/Demo/rpc/README b/sys/src/cmd/python/Demo/rpc/README
new file mode 100644
index 000000000..97948a338
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/README
@@ -0,0 +1,31 @@
+This is a Python interface to Sun RPC, designed and implemented mostly
+by reading the Internet RFCs about the subject.
+
+*** NOTE: xdr.py has evolved into the standard module xdrlib.py ***
+
+There are two library modules, xdr.py and rpc.py, and several example
+clients: mountclient.py, nfsclient.py, and rnusersclient.py,
+implementing the NFS Mount protocol, (part of) the NFS protocol, and
+the "rnusers" protocol (used by rusers(1)), respectively. The latter
+demonstrates the use of broadcast via the Port mapper's CALLIT
+procedure.
+
+There is also a way to create servers in Python.
+
+To test the nfs client, run it from the shell with something like this:
+
+ python -c 'import nfsclient; nfsclient.test()' [hostname [filesystemname]]
+
+When called without a filesystemname, it lists the filesystems at the
+host; default host is the local machine.
+
+Other clients are tested similarly.
+
+For hostname, use e.g. wuarchive.wustl.edu or gatekeeper.dec.com (two
+hosts that are known to export NFS filesystems with little restrictions).
+
+There are now two different RPC compilers:
+
+1) Wim Lewis rpcgen.py found on http://www.omnigroup.com/~wiml/soft/stale-index.html#python.
+
+2) Peter Åstrands rpcgen.py, which is part of "pynfs" (http://www.cendio.se/~peter/pynfs/).
diff --git a/sys/src/cmd/python/Demo/rpc/T.py b/sys/src/cmd/python/Demo/rpc/T.py
new file mode 100644
index 000000000..33255073e
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/T.py
@@ -0,0 +1,22 @@
+# Simple interface to report execution times of program fragments.
+# Call TSTART() to reset the timer, TSTOP(...) to report times.
+
+import sys, os, time
+
+def TSTART():
+ global t0, t1
+ u, s, cu, cs = os.times()
+ t0 = u+cu, s+cs, time.time()
+
+def TSTOP(*label):
+ global t0, t1
+ u, s, cu, cs = os.times()
+ t1 = u+cu, s+cs, time.time()
+ tt = []
+ for i in range(3):
+ tt.append(t1[i] - t0[i])
+ [u, s, r] = tt
+ msg = ''
+ for x in label: msg = msg + (x + ' ')
+ msg = msg + '%r user, %r sys, %r real\n' % (u, s, r)
+ sys.stderr.write(msg)
diff --git a/sys/src/cmd/python/Demo/rpc/mountclient.py b/sys/src/cmd/python/Demo/rpc/mountclient.py
new file mode 100644
index 000000000..318a9d99b
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/mountclient.py
@@ -0,0 +1,202 @@
+# Mount RPC client -- RFC 1094 (NFS), Appendix A
+
+# This module demonstrates how to write your own RPC client in Python.
+# When this example was written, there was no RPC compiler for
+# Python. Without such a compiler, you must first create classes
+# derived from Packer and Unpacker to handle the data types for the
+# server you want to interface to. You then write the client class.
+# If you want to support both the TCP and the UDP version of a
+# protocol, use multiple inheritance as shown below.
+
+
+import rpc
+from rpc import Packer, Unpacker, TCPClient, UDPClient
+
+
+# Program number and version for the mount protocol
+MOUNTPROG = 100005
+MOUNTVERS = 1
+
+# Size of the 'fhandle' opaque structure
+FHSIZE = 32
+
+
+# Packer derived class for Mount protocol clients.
+# The only thing we need to pack beyond basic types is an 'fhandle'
+
+class MountPacker(Packer):
+
+ def pack_fhandle(self, fhandle):
+ self.pack_fopaque(FHSIZE, fhandle)
+
+
+# Unpacker derived class for Mount protocol clients.
+# The important types we need to unpack are fhandle, fhstatus,
+# mountlist and exportlist; mountstruct, exportstruct and groups are
+# used to unpack components of mountlist and exportlist and the
+# corresponding functions are passed as function argument to the
+# generic unpack_list function.
+
+class MountUnpacker(Unpacker):
+
+ def unpack_fhandle(self):
+ return self.unpack_fopaque(FHSIZE)
+
+ def unpack_fhstatus(self):
+ status = self.unpack_uint()
+ if status == 0:
+ fh = self.unpack_fhandle()
+ else:
+ fh = None
+ return status, fh
+
+ def unpack_mountlist(self):
+ return self.unpack_list(self.unpack_mountstruct)
+
+ def unpack_mountstruct(self):
+ hostname = self.unpack_string()
+ directory = self.unpack_string()
+ return (hostname, directory)
+
+ def unpack_exportlist(self):
+ return self.unpack_list(self.unpack_exportstruct)
+
+ def unpack_exportstruct(self):
+ filesys = self.unpack_string()
+ groups = self.unpack_groups()
+ return (filesys, groups)
+
+ def unpack_groups(self):
+ return self.unpack_list(self.unpack_string)
+
+
+# These are the procedures specific to the Mount client class.
+# Think of this as a derived class of either TCPClient or UDPClient.
+
+class PartialMountClient:
+
+ # This method is called by Client.__init__ to initialize
+ # self.packer and self.unpacker
+ def addpackers(self):
+ self.packer = MountPacker()
+ self.unpacker = MountUnpacker('')
+
+ # This method is called by Client.__init__ to bind the socket
+ # to a particular network interface and port. We use the
+ # default network interface, but if we're running as root,
+ # we want to bind to a reserved port
+ def bindsocket(self):
+ import os
+ try:
+ uid = os.getuid()
+ except AttributeError:
+ uid = 1
+ if uid == 0:
+ port = rpc.bindresvport(self.sock, '')
+ # 'port' is not used
+ else:
+ self.sock.bind(('', 0))
+
+ # This function is called to cough up a suitable
+ # authentication object for a call to procedure 'proc'.
+ def mkcred(self):
+ if self.cred == None:
+ self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default()
+ return self.cred
+
+ # The methods Mnt, Dump etc. each implement one Remote
+ # Procedure Call. This is done by calling self.make_call()
+ # with as arguments:
+ #
+ # - the procedure number
+ # - the arguments (or None)
+ # - the "packer" function for the arguments (or None)
+ # - the "unpacker" function for the return value (or None)
+ #
+ # The packer and unpacker function, if not None, *must* be
+ # methods of self.packer and self.unpacker, respectively.
+ # A value of None means that there are no arguments or is no
+ # return value, respectively.
+ #
+ # The return value from make_call() is the return value from
+ # the remote procedure call, as unpacked by the "unpacker"
+ # function, or None if the unpacker function is None.
+ #
+ # (Even if you expect a result of None, you should still
+ # return the return value from make_call(), since this may be
+ # needed by a broadcasting version of the class.)
+ #
+ # If the call fails, make_call() raises an exception
+ # (this includes time-outs and invalid results).
+ #
+ # Note that (at least with the UDP protocol) there is no
+ # guarantee that a call is executed at most once. When you do
+ # get a reply, you know it has been executed at least once;
+ # when you don't get a reply, you know nothing.
+
+ def Mnt(self, directory):
+ return self.make_call(1, directory, \
+ self.packer.pack_string, \
+ self.unpacker.unpack_fhstatus)
+
+ def Dump(self):
+ return self.make_call(2, None, \
+ None, self.unpacker.unpack_mountlist)
+
+ def Umnt(self, directory):
+ return self.make_call(3, directory, \
+ self.packer.pack_string, None)
+
+ def Umntall(self):
+ return self.make_call(4, None, None, None)
+
+ def Export(self):
+ return self.make_call(5, None, \
+ None, self.unpacker.unpack_exportlist)
+
+
+# We turn the partial Mount client into a full one for either protocol
+# by use of multiple inheritance. (In general, when class C has base
+# classes B1...Bn, if x is an instance of class C, methods of x are
+# searched first in C, then in B1, then in B2, ..., finally in Bn.)
+
+class TCPMountClient(PartialMountClient, TCPClient):
+
+ def __init__(self, host):
+ TCPClient.__init__(self, host, MOUNTPROG, MOUNTVERS)
+
+
+class UDPMountClient(PartialMountClient, UDPClient):
+
+ def __init__(self, host):
+ UDPClient.__init__(self, host, MOUNTPROG, MOUNTVERS)
+
+
+# A little test program for the Mount client. This takes a host as
+# command line argument (default the local machine), prints its export
+# list, and attempts to mount and unmount each exported files system.
+# An optional first argument of -t or -u specifies the protocol to use
+# (TCP or UDP), default is UDP.
+
+def test():
+ import sys
+ if sys.argv[1:] and sys.argv[1] == '-t':
+ C = TCPMountClient
+ del sys.argv[1]
+ elif sys.argv[1:] and sys.argv[1] == '-u':
+ C = UDPMountClient
+ del sys.argv[1]
+ else:
+ C = UDPMountClient
+ if sys.argv[1:]: host = sys.argv[1]
+ else: host = ''
+ mcl = C(host)
+ list = mcl.Export()
+ for item in list:
+ print item
+ try:
+ mcl.Mnt(item[0])
+ except:
+ print 'Sorry'
+ continue
+ mcl.Umnt(item[0])
diff --git a/sys/src/cmd/python/Demo/rpc/nfsclient.py b/sys/src/cmd/python/Demo/rpc/nfsclient.py
new file mode 100644
index 000000000..c4387b4a0
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/nfsclient.py
@@ -0,0 +1,201 @@
+# NFS RPC client -- RFC 1094
+
+# XXX This is not yet complete.
+# XXX Only GETATTR, SETTTR, LOOKUP and READDIR are supported.
+
+# (See mountclient.py for some hints on how to write RPC clients in
+# Python in general)
+
+import rpc
+from rpc import UDPClient, TCPClient
+from mountclient import FHSIZE, MountPacker, MountUnpacker
+
+NFS_PROGRAM = 100003
+NFS_VERSION = 2
+
+# enum stat
+NFS_OK = 0
+# (...many error values...)
+
+# enum ftype
+NFNON = 0
+NFREG = 1
+NFDIR = 2
+NFBLK = 3
+NFCHR = 4
+NFLNK = 5
+
+
+class NFSPacker(MountPacker):
+
+ def pack_sattrargs(self, sa):
+ file, attributes = sa
+ self.pack_fhandle(file)
+ self.pack_sattr(attributes)
+
+ def pack_sattr(self, sa):
+ mode, uid, gid, size, atime, mtime = sa
+ self.pack_uint(mode)
+ self.pack_uint(uid)
+ self.pack_uint(gid)
+ self.pack_uint(size)
+ self.pack_timeval(atime)
+ self.pack_timeval(mtime)
+
+ def pack_diropargs(self, da):
+ dir, name = da
+ self.pack_fhandle(dir)
+ self.pack_string(name)
+
+ def pack_readdirargs(self, ra):
+ dir, cookie, count = ra
+ self.pack_fhandle(dir)
+ self.pack_uint(cookie)
+ self.pack_uint(count)
+
+ def pack_timeval(self, tv):
+ secs, usecs = tv
+ self.pack_uint(secs)
+ self.pack_uint(usecs)
+
+
+class NFSUnpacker(MountUnpacker):
+
+ def unpack_readdirres(self):
+ status = self.unpack_enum()
+ if status == NFS_OK:
+ entries = self.unpack_list(self.unpack_entry)
+ eof = self.unpack_bool()
+ rest = (entries, eof)
+ else:
+ rest = None
+ return (status, rest)
+
+ def unpack_entry(self):
+ fileid = self.unpack_uint()
+ name = self.unpack_string()
+ cookie = self.unpack_uint()
+ return (fileid, name, cookie)
+
+ def unpack_diropres(self):
+ status = self.unpack_enum()
+ if status == NFS_OK:
+ fh = self.unpack_fhandle()
+ fa = self.unpack_fattr()
+ rest = (fh, fa)
+ else:
+ rest = None
+ return (status, rest)
+
+ def unpack_attrstat(self):
+ status = self.unpack_enum()
+ if status == NFS_OK:
+ attributes = self.unpack_fattr()
+ else:
+ attributes = None
+ return status, attributes
+
+ def unpack_fattr(self):
+ type = self.unpack_enum()
+ mode = self.unpack_uint()
+ nlink = self.unpack_uint()
+ uid = self.unpack_uint()
+ gid = self.unpack_uint()
+ size = self.unpack_uint()
+ blocksize = self.unpack_uint()
+ rdev = self.unpack_uint()
+ blocks = self.unpack_uint()
+ fsid = self.unpack_uint()
+ fileid = self.unpack_uint()
+ atime = self.unpack_timeval()
+ mtime = self.unpack_timeval()
+ ctime = self.unpack_timeval()
+ return (type, mode, nlink, uid, gid, size, blocksize, \
+ rdev, blocks, fsid, fileid, atime, mtime, ctime)
+
+ def unpack_timeval(self):
+ secs = self.unpack_uint()
+ usecs = self.unpack_uint()
+ return (secs, usecs)
+
+
+class NFSClient(UDPClient):
+
+ def __init__(self, host):
+ UDPClient.__init__(self, host, NFS_PROGRAM, NFS_VERSION)
+
+ def addpackers(self):
+ self.packer = NFSPacker()
+ self.unpacker = NFSUnpacker('')
+
+ def mkcred(self):
+ if self.cred == None:
+ self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default()
+ return self.cred
+
+ def Getattr(self, fh):
+ return self.make_call(1, fh, \
+ self.packer.pack_fhandle, \
+ self.unpacker.unpack_attrstat)
+
+ def Setattr(self, sa):
+ return self.make_call(2, sa, \
+ self.packer.pack_sattrargs, \
+ self.unpacker.unpack_attrstat)
+
+ # Root() is obsolete
+
+ def Lookup(self, da):
+ return self.make_call(4, da, \
+ self.packer.pack_diropargs, \
+ self.unpacker.unpack_diropres)
+
+ # ...
+
+ def Readdir(self, ra):
+ return self.make_call(16, ra, \
+ self.packer.pack_readdirargs, \
+ self.unpacker.unpack_readdirres)
+
+ # Shorthand to get the entire contents of a directory
+ def Listdir(self, dir):
+ list = []
+ ra = (dir, 0, 2000)
+ while 1:
+ (status, rest) = self.Readdir(ra)
+ if status <> NFS_OK:
+ break
+ entries, eof = rest
+ last_cookie = None
+ for fileid, name, cookie in entries:
+ list.append((fileid, name))
+ last_cookie = cookie
+ if eof or last_cookie == None:
+ break
+ ra = (ra[0], last_cookie, ra[2])
+ return list
+
+
+def test():
+ import sys
+ if sys.argv[1:]: host = sys.argv[1]
+ else: host = ''
+ if sys.argv[2:]: filesys = sys.argv[2]
+ else: filesys = None
+ from mountclient import UDPMountClient, TCPMountClient
+ mcl = TCPMountClient(host)
+ if filesys == None:
+ list = mcl.Export()
+ for item in list:
+ print item
+ return
+ sf = mcl.Mnt(filesys)
+ print sf
+ fh = sf[1]
+ if fh:
+ ncl = NFSClient(host)
+ as = ncl.Getattr(fh)
+ print as
+ list = ncl.Listdir(fh)
+ for item in list: print item
+ mcl.Umnt(filesys)
diff --git a/sys/src/cmd/python/Demo/rpc/rnusersclient.py b/sys/src/cmd/python/Demo/rpc/rnusersclient.py
new file mode 100644
index 000000000..1f3a882c5
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/rnusersclient.py
@@ -0,0 +1,98 @@
+# Remote nusers client interface
+
+import rpc
+from rpc import Packer, Unpacker, UDPClient, BroadcastUDPClient
+
+
+class RnusersPacker(Packer):
+ def pack_utmp(self, ui):
+ ut_line, ut_name, ut_host, ut_time = utmp
+ self.pack_string(ut_line)
+ self.pack_string(ut_name)
+ self.pack_string(ut_host)
+ self.pack_int(ut_time)
+ def pack_utmpidle(self, ui):
+ ui_itmp, ui_idle = ui
+ self.pack_utmp(ui_utmp)
+ self.pack_uint(ui_idle)
+ def pack_utmpidlearr(self, list):
+ self.pack_array(list, self.pack_itmpidle)
+
+
+class RnusersUnpacker(Unpacker):
+ def unpack_utmp(self):
+ ut_line = self.unpack_string()
+ ut_name = self.unpack_string()
+ ut_host = self.unpack_string()
+ ut_time = self.unpack_int()
+ return ut_line, ut_name, ut_host, ut_time
+ def unpack_utmpidle(self):
+ ui_utmp = self.unpack_utmp()
+ ui_idle = self.unpack_uint()
+ return ui_utmp, ui_idle
+ def unpack_utmpidlearr(self):
+ return self.unpack_array(self.unpack_utmpidle)
+
+
+class PartialRnusersClient:
+
+ def addpackers(self):
+ self.packer = RnusersPacker()
+ self.unpacker = RnusersUnpacker('')
+
+ def Num(self):
+ return self.make_call(1, None, None, self.unpacker.unpack_int)
+
+ def Names(self):
+ return self.make_call(2, None, \
+ None, self.unpacker.unpack_utmpidlearr)
+
+ def Allnames(self):
+ return self.make_call(3, None, \
+ None, self.unpacker.unpack_utmpidlearr)
+
+
+class RnusersClient(PartialRnusersClient, UDPClient):
+
+ def __init__(self, host):
+ UDPClient.__init__(self, host, 100002, 2)
+
+
+class BroadcastRnusersClient(PartialRnusersClient, BroadcastUDPClient):
+
+ def __init__(self, bcastaddr):
+ BroadcastUDPClient.__init__(self, bcastaddr, 100002, 2)
+
+
+def test():
+ import sys
+ if not sys.argv[1:]:
+ testbcast()
+ return
+ else:
+ host = sys.argv[1]
+ c = RnusersClient(host)
+ list = c.Names()
+ for (line, name, host, time), idle in list:
+ line = strip0(line)
+ name = strip0(name)
+ host = strip0(host)
+ print "%r %r %r %s %s" % (name, host, line, time, idle)
+
+def testbcast():
+ c = BroadcastRnusersClient('<broadcast>')
+ def listit(list, fromaddr):
+ host, port = fromaddr
+ print host + '\t:',
+ for (line, name, host, time), idle in list:
+ print strip0(name),
+ print
+ c.set_reply_handler(listit)
+ all = c.Names()
+ print 'Total Count:', len(all)
+
+def strip0(s):
+ while s and s[-1] == '\0': s = s[:-1]
+ return s
+
+test()
diff --git a/sys/src/cmd/python/Demo/rpc/rpc.py b/sys/src/cmd/python/Demo/rpc/rpc.py
new file mode 100644
index 000000000..141fe09a5
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/rpc.py
@@ -0,0 +1,893 @@
+# Sun RPC version 2 -- RFC1057.
+
+# XXX There should be separate exceptions for the various reasons why
+# XXX an RPC can fail, rather than using RuntimeError for everything
+
+# XXX Need to use class based exceptions rather than string exceptions
+
+# XXX The UDP version of the protocol resends requests when it does
+# XXX not receive a timely reply -- use only for idempotent calls!
+
+# XXX There is no provision for call timeout on TCP connections
+
+import xdr
+import socket
+import os
+
+RPCVERSION = 2
+
+CALL = 0
+REPLY = 1
+
+AUTH_NULL = 0
+AUTH_UNIX = 1
+AUTH_SHORT = 2
+AUTH_DES = 3
+
+MSG_ACCEPTED = 0
+MSG_DENIED = 1
+
+SUCCESS = 0 # RPC executed successfully
+PROG_UNAVAIL = 1 # remote hasn't exported program
+PROG_MISMATCH = 2 # remote can't support version #
+PROC_UNAVAIL = 3 # program can't support procedure
+GARBAGE_ARGS = 4 # procedure can't decode params
+
+RPC_MISMATCH = 0 # RPC version number != 2
+AUTH_ERROR = 1 # remote can't authenticate caller
+
+AUTH_BADCRED = 1 # bad credentials (seal broken)
+AUTH_REJECTEDCRED = 2 # client must begin new session
+AUTH_BADVERF = 3 # bad verifier (seal broken)
+AUTH_REJECTEDVERF = 4 # verifier expired or replayed
+AUTH_TOOWEAK = 5 # rejected for security reasons
+
+
+class Packer(xdr.Packer):
+
+ def pack_auth(self, auth):
+ flavor, stuff = auth
+ self.pack_enum(flavor)
+ self.pack_opaque(stuff)
+
+ def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
+ self.pack_uint(stamp)
+ self.pack_string(machinename)
+ self.pack_uint(uid)
+ self.pack_uint(gid)
+ self.pack_uint(len(gids))
+ for i in gids:
+ self.pack_uint(i)
+
+ def pack_callheader(self, xid, prog, vers, proc, cred, verf):
+ self.pack_uint(xid)
+ self.pack_enum(CALL)
+ self.pack_uint(RPCVERSION)
+ self.pack_uint(prog)
+ self.pack_uint(vers)
+ self.pack_uint(proc)
+ self.pack_auth(cred)
+ self.pack_auth(verf)
+ # Caller must add procedure-specific part of call
+
+ def pack_replyheader(self, xid, verf):
+ self.pack_uint(xid)
+ self.pack_enum(REPLY)
+ self.pack_uint(MSG_ACCEPTED)
+ self.pack_auth(verf)
+ self.pack_enum(SUCCESS)
+ # Caller must add procedure-specific part of reply
+
+
+# Exceptions
+BadRPCFormat = 'rpc.BadRPCFormat'
+BadRPCVersion = 'rpc.BadRPCVersion'
+GarbageArgs = 'rpc.GarbageArgs'
+
+class Unpacker(xdr.Unpacker):
+
+ def unpack_auth(self):
+ flavor = self.unpack_enum()
+ stuff = self.unpack_opaque()
+ return (flavor, stuff)
+
+ def unpack_callheader(self):
+ xid = self.unpack_uint()
+ temp = self.unpack_enum()
+ if temp != CALL:
+ raise BadRPCFormat, 'no CALL but %r' % (temp,)
+ temp = self.unpack_uint()
+ if temp != RPCVERSION:
+ raise BadRPCVersion, 'bad RPC version %r' % (temp,)
+ prog = self.unpack_uint()
+ vers = self.unpack_uint()
+ proc = self.unpack_uint()
+ cred = self.unpack_auth()
+ verf = self.unpack_auth()
+ return xid, prog, vers, proc, cred, verf
+ # Caller must add procedure-specific part of call
+
+ def unpack_replyheader(self):
+ xid = self.unpack_uint()
+ mtype = self.unpack_enum()
+ if mtype != REPLY:
+ raise RuntimeError, 'no REPLY but %r' % (mtype,)
+ stat = self.unpack_enum()
+ if stat == MSG_DENIED:
+ stat = self.unpack_enum()
+ if stat == RPC_MISMATCH:
+ low = self.unpack_uint()
+ high = self.unpack_uint()
+ raise RuntimeError, \
+ 'MSG_DENIED: RPC_MISMATCH: %r' % ((low, high),)
+ if stat == AUTH_ERROR:
+ stat = self.unpack_uint()
+ raise RuntimeError, \
+ 'MSG_DENIED: AUTH_ERROR: %r' % (stat,)
+ raise RuntimeError, 'MSG_DENIED: %r' % (stat,)
+ if stat != MSG_ACCEPTED:
+ raise RuntimeError, \
+ 'Neither MSG_DENIED nor MSG_ACCEPTED: %r' % (stat,)
+ verf = self.unpack_auth()
+ stat = self.unpack_enum()
+ if stat == PROG_UNAVAIL:
+ raise RuntimeError, 'call failed: PROG_UNAVAIL'
+ if stat == PROG_MISMATCH:
+ low = self.unpack_uint()
+ high = self.unpack_uint()
+ raise RuntimeError, \
+ 'call failed: PROG_MISMATCH: %r' % ((low, high),)
+ if stat == PROC_UNAVAIL:
+ raise RuntimeError, 'call failed: PROC_UNAVAIL'
+ if stat == GARBAGE_ARGS:
+ raise RuntimeError, 'call failed: GARBAGE_ARGS'
+ if stat != SUCCESS:
+ raise RuntimeError, 'call failed: %r' % (stat,)
+ return xid, verf
+ # Caller must get procedure-specific part of reply
+
+
+# Subroutines to create opaque authentication objects
+
+def make_auth_null():
+ return ''
+
+def make_auth_unix(seed, host, uid, gid, groups):
+ p = Packer()
+ p.pack_auth_unix(seed, host, uid, gid, groups)
+ return p.get_buf()
+
+def make_auth_unix_default():
+ try:
+ from os import getuid, getgid
+ uid = getuid()
+ gid = getgid()
+ except ImportError:
+ uid = gid = 0
+ import time
+ return make_auth_unix(int(time.time()-unix_epoch()), \
+ socket.gethostname(), uid, gid, [])
+
+_unix_epoch = -1
+def unix_epoch():
+ """Very painful calculation of when the Unix Epoch is.
+
+ This is defined as the return value of time.time() on Jan 1st,
+ 1970, 00:00:00 GMT.
+
+ On a Unix system, this should always return 0.0. On a Mac, the
+ calculations are needed -- and hard because of integer overflow
+ and other limitations.
+
+ """
+ global _unix_epoch
+ if _unix_epoch >= 0: return _unix_epoch
+ import time
+ now = time.time()
+ localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...)
+ gmt = time.gmtime(now)
+ offset = time.mktime(localt) - time.mktime(gmt)
+ y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0
+ offset, ss = divmod(ss + offset, 60)
+ offset, mm = divmod(mm + offset, 60)
+ offset, hh = divmod(hh + offset, 24)
+ d = d + offset
+ _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0))
+ print "Unix epoch:", time.ctime(_unix_epoch)
+ return _unix_epoch
+
+
+# Common base class for clients
+
+class Client:
+
+ def __init__(self, host, prog, vers, port):
+ self.host = host
+ self.prog = prog
+ self.vers = vers
+ self.port = port
+ self.makesocket() # Assigns to self.sock
+ self.bindsocket()
+ self.connsocket()
+ self.lastxid = 0 # XXX should be more random?
+ self.addpackers()
+ self.cred = None
+ self.verf = None
+
+ def close(self):
+ self.sock.close()
+
+ def makesocket(self):
+ # This MUST be overridden
+ raise RuntimeError, 'makesocket not defined'
+
+ def connsocket(self):
+ # Override this if you don't want/need a connection
+ self.sock.connect((self.host, self.port))
+
+ def bindsocket(self):
+ # Override this to bind to a different port (e.g. reserved)
+ self.sock.bind(('', 0))
+
+ def addpackers(self):
+ # Override this to use derived classes from Packer/Unpacker
+ self.packer = Packer()
+ self.unpacker = Unpacker('')
+
+ def make_call(self, proc, args, pack_func, unpack_func):
+ # Don't normally override this (but see Broadcast)
+ if pack_func is None and args is not None:
+ raise TypeError, 'non-null args with null pack_func'
+ self.start_call(proc)
+ if pack_func:
+ pack_func(args)
+ self.do_call()
+ if unpack_func:
+ result = unpack_func()
+ else:
+ result = None
+ self.unpacker.done()
+ return result
+
+ def start_call(self, proc):
+ # Don't override this
+ self.lastxid = xid = self.lastxid + 1
+ cred = self.mkcred()
+ verf = self.mkverf()
+ p = self.packer
+ p.reset()
+ p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
+
+ def do_call(self):
+ # This MUST be overridden
+ raise RuntimeError, 'do_call not defined'
+
+ def mkcred(self):
+ # Override this to use more powerful credentials
+ if self.cred == None:
+ self.cred = (AUTH_NULL, make_auth_null())
+ return self.cred
+
+ def mkverf(self):
+ # Override this to use a more powerful verifier
+ if self.verf == None:
+ self.verf = (AUTH_NULL, make_auth_null())
+ return self.verf
+
+ def call_0(self): # Procedure 0 is always like this
+ return self.make_call(0, None, None, None)
+
+
+# Record-Marking standard support
+
+def sendfrag(sock, last, frag):
+ x = len(frag)
+ if last: x = x | 0x80000000L
+ header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
+ chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
+ sock.send(header + frag)
+
+def sendrecord(sock, record):
+ sendfrag(sock, 1, record)
+
+def recvfrag(sock):
+ header = sock.recv(4)
+ if len(header) < 4:
+ raise EOFError
+ x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
+ ord(header[2])<<8 | ord(header[3])
+ last = ((x & 0x80000000) != 0)
+ n = int(x & 0x7fffffff)
+ frag = ''
+ while n > 0:
+ buf = sock.recv(n)
+ if not buf: raise EOFError
+ n = n - len(buf)
+ frag = frag + buf
+ return last, frag
+
+def recvrecord(sock):
+ record = ''
+ last = 0
+ while not last:
+ last, frag = recvfrag(sock)
+ record = record + frag
+ return record
+
+
+# Try to bind to a reserved port (must be root)
+
+last_resv_port_tried = None
+def bindresvport(sock, host):
+ global last_resv_port_tried
+ FIRST, LAST = 600, 1024 # Range of ports to try
+ if last_resv_port_tried == None:
+ import os
+ last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST)
+ for i in range(last_resv_port_tried, LAST) + \
+ range(FIRST, last_resv_port_tried):
+ last_resv_port_tried = i
+ try:
+ sock.bind((host, i))
+ return last_resv_port_tried
+ except socket.error, (errno, msg):
+ if errno != 114:
+ raise socket.error, (errno, msg)
+ raise RuntimeError, 'can\'t assign reserved port'
+
+
+# Client using TCP to a specific port
+
+class RawTCPClient(Client):
+
+ def makesocket(self):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+ def do_call(self):
+ call = self.packer.get_buf()
+ sendrecord(self.sock, call)
+ reply = recvrecord(self.sock)
+ u = self.unpacker
+ u.reset(reply)
+ xid, verf = u.unpack_replyheader()
+ if xid != self.lastxid:
+ # Can't really happen since this is TCP...
+ raise RuntimeError, 'wrong xid in reply %r instead of %r' % (
+ xid, self.lastxid)
+
+
+# Client using UDP to a specific port
+
+class RawUDPClient(Client):
+
+ def makesocket(self):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+ def do_call(self):
+ call = self.packer.get_buf()
+ self.sock.send(call)
+ try:
+ from select import select
+ except ImportError:
+ print 'WARNING: select not found, RPC may hang'
+ select = None
+ BUFSIZE = 8192 # Max UDP buffer size
+ timeout = 1
+ count = 5
+ while 1:
+ r, w, x = [self.sock], [], []
+ if select:
+ r, w, x = select(r, w, x, timeout)
+ if self.sock not in r:
+ count = count - 1
+ if count < 0: raise RuntimeError, 'timeout'
+ if timeout < 25: timeout = timeout *2
+## print 'RESEND', timeout, count
+ self.sock.send(call)
+ continue
+ reply = self.sock.recv(BUFSIZE)
+ u = self.unpacker
+ u.reset(reply)
+ xid, verf = u.unpack_replyheader()
+ if xid != self.lastxid:
+## print 'BAD xid'
+ continue
+ break
+
+
+# Client using UDP broadcast to a specific port
+
+class RawBroadcastUDPClient(RawUDPClient):
+
+ def __init__(self, bcastaddr, prog, vers, port):
+ RawUDPClient.__init__(self, bcastaddr, prog, vers, port)
+ self.reply_handler = None
+ self.timeout = 30
+
+ def connsocket(self):
+ # Don't connect -- use sendto
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+
+ def set_reply_handler(self, reply_handler):
+ self.reply_handler = reply_handler
+
+ def set_timeout(self, timeout):
+ self.timeout = timeout # Use None for infinite timeout
+
+ def make_call(self, proc, args, pack_func, unpack_func):
+ if pack_func is None and args is not None:
+ raise TypeError, 'non-null args with null pack_func'
+ self.start_call(proc)
+ if pack_func:
+ pack_func(args)
+ call = self.packer.get_buf()
+ self.sock.sendto(call, (self.host, self.port))
+ try:
+ from select import select
+ except ImportError:
+ print 'WARNING: select not found, broadcast will hang'
+ select = None
+ BUFSIZE = 8192 # Max UDP buffer size (for reply)
+ replies = []
+ if unpack_func is None:
+ def dummy(): pass
+ unpack_func = dummy
+ while 1:
+ r, w, x = [self.sock], [], []
+ if select:
+ if self.timeout is None:
+ r, w, x = select(r, w, x)
+ else:
+ r, w, x = select(r, w, x, self.timeout)
+ if self.sock not in r:
+ break
+ reply, fromaddr = self.sock.recvfrom(BUFSIZE)
+ u = self.unpacker
+ u.reset(reply)
+ xid, verf = u.unpack_replyheader()
+ if xid != self.lastxid:
+## print 'BAD xid'
+ continue
+ reply = unpack_func()
+ self.unpacker.done()
+ replies.append((reply, fromaddr))
+ if self.reply_handler:
+ self.reply_handler(reply, fromaddr)
+ return replies
+
+
+# Port mapper interface
+
+# Program number, version and (fixed!) port number
+PMAP_PROG = 100000
+PMAP_VERS = 2
+PMAP_PORT = 111
+
+# Procedure numbers
+PMAPPROC_NULL = 0 # (void) -> void
+PMAPPROC_SET = 1 # (mapping) -> bool
+PMAPPROC_UNSET = 2 # (mapping) -> bool
+PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
+PMAPPROC_DUMP = 4 # (void) -> pmaplist
+PMAPPROC_CALLIT = 5 # (call_args) -> call_result
+
+# A mapping is (prog, vers, prot, port) and prot is one of:
+
+IPPROTO_TCP = 6
+IPPROTO_UDP = 17
+
+# A pmaplist is a variable-length list of mappings, as follows:
+# either (1, mapping, pmaplist) or (0).
+
+# A call_args is (prog, vers, proc, args) where args is opaque;
+# a call_result is (port, res) where res is opaque.
+
+
+class PortMapperPacker(Packer):
+
+ def pack_mapping(self, mapping):
+ prog, vers, prot, port = mapping
+ self.pack_uint(prog)
+ self.pack_uint(vers)
+ self.pack_uint(prot)
+ self.pack_uint(port)
+
+ def pack_pmaplist(self, list):
+ self.pack_list(list, self.pack_mapping)
+
+ def pack_call_args(self, ca):
+ prog, vers, proc, args = ca
+ self.pack_uint(prog)
+ self.pack_uint(vers)
+ self.pack_uint(proc)
+ self.pack_opaque(args)
+
+
+class PortMapperUnpacker(Unpacker):
+
+ def unpack_mapping(self):
+ prog = self.unpack_uint()
+ vers = self.unpack_uint()
+ prot = self.unpack_uint()
+ port = self.unpack_uint()
+ return prog, vers, prot, port
+
+ def unpack_pmaplist(self):
+ return self.unpack_list(self.unpack_mapping)
+
+ def unpack_call_result(self):
+ port = self.unpack_uint()
+ res = self.unpack_opaque()
+ return port, res
+
+
+class PartialPortMapperClient:
+
+ def addpackers(self):
+ self.packer = PortMapperPacker()
+ self.unpacker = PortMapperUnpacker('')
+
+ def Set(self, mapping):
+ return self.make_call(PMAPPROC_SET, mapping, \
+ self.packer.pack_mapping, \
+ self.unpacker.unpack_uint)
+
+ def Unset(self, mapping):
+ return self.make_call(PMAPPROC_UNSET, mapping, \
+ self.packer.pack_mapping, \
+ self.unpacker.unpack_uint)
+
+ def Getport(self, mapping):
+ return self.make_call(PMAPPROC_GETPORT, mapping, \
+ self.packer.pack_mapping, \
+ self.unpacker.unpack_uint)
+
+ def Dump(self):
+ return self.make_call(PMAPPROC_DUMP, None, \
+ None, \
+ self.unpacker.unpack_pmaplist)
+
+ def Callit(self, ca):
+ return self.make_call(PMAPPROC_CALLIT, ca, \
+ self.packer.pack_call_args, \
+ self.unpacker.unpack_call_result)
+
+
+class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
+
+ def __init__(self, host):
+ RawTCPClient.__init__(self, \
+ host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
+
+ def __init__(self, host):
+ RawUDPClient.__init__(self, \
+ host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+class BroadcastUDPPortMapperClient(PartialPortMapperClient, \
+ RawBroadcastUDPClient):
+
+ def __init__(self, bcastaddr):
+ RawBroadcastUDPClient.__init__(self, \
+ bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT)
+
+
+# Generic clients that find their server through the Port mapper
+
+class TCPClient(RawTCPClient):
+
+ def __init__(self, host, prog, vers):
+ pmap = TCPPortMapperClient(host)
+ port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
+ pmap.close()
+ if port == 0:
+ raise RuntimeError, 'program not registered'
+ RawTCPClient.__init__(self, host, prog, vers, port)
+
+
+class UDPClient(RawUDPClient):
+
+ def __init__(self, host, prog, vers):
+ pmap = UDPPortMapperClient(host)
+ port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
+ pmap.close()
+ if port == 0:
+ raise RuntimeError, 'program not registered'
+ RawUDPClient.__init__(self, host, prog, vers, port)
+
+
+class BroadcastUDPClient(Client):
+
+ def __init__(self, bcastaddr, prog, vers):
+ self.pmap = BroadcastUDPPortMapperClient(bcastaddr)
+ self.pmap.set_reply_handler(self.my_reply_handler)
+ self.prog = prog
+ self.vers = vers
+ self.user_reply_handler = None
+ self.addpackers()
+
+ def close(self):
+ self.pmap.close()
+
+ def set_reply_handler(self, reply_handler):
+ self.user_reply_handler = reply_handler
+
+ def set_timeout(self, timeout):
+ self.pmap.set_timeout(timeout)
+
+ def my_reply_handler(self, reply, fromaddr):
+ port, res = reply
+ self.unpacker.reset(res)
+ result = self.unpack_func()
+ self.unpacker.done()
+ self.replies.append((result, fromaddr))
+ if self.user_reply_handler is not None:
+ self.user_reply_handler(result, fromaddr)
+
+ def make_call(self, proc, args, pack_func, unpack_func):
+ self.packer.reset()
+ if pack_func:
+ pack_func(args)
+ if unpack_func is None:
+ def dummy(): pass
+ self.unpack_func = dummy
+ else:
+ self.unpack_func = unpack_func
+ self.replies = []
+ packed_args = self.packer.get_buf()
+ dummy_replies = self.pmap.Callit( \
+ (self.prog, self.vers, proc, packed_args))
+ return self.replies
+
+
+# Server classes
+
+# These are not symmetric to the Client classes
+# XXX No attempt is made to provide authorization hooks yet
+
+class Server:
+
+ def __init__(self, host, prog, vers, port):
+ self.host = host # Should normally be '' for default interface
+ self.prog = prog
+ self.vers = vers
+ self.port = port # Should normally be 0 for random port
+ self.makesocket() # Assigns to self.sock and self.prot
+ self.bindsocket()
+ self.host, self.port = self.sock.getsockname()
+ self.addpackers()
+
+ def register(self):
+ mapping = self.prog, self.vers, self.prot, self.port
+ p = TCPPortMapperClient(self.host)
+ if not p.Set(mapping):
+ raise RuntimeError, 'register failed'
+
+ def unregister(self):
+ mapping = self.prog, self.vers, self.prot, self.port
+ p = TCPPortMapperClient(self.host)
+ if not p.Unset(mapping):
+ raise RuntimeError, 'unregister failed'
+
+ def handle(self, call):
+ # Don't use unpack_header but parse the header piecewise
+ # XXX I have no idea if I am using the right error responses!
+ self.unpacker.reset(call)
+ self.packer.reset()
+ xid = self.unpacker.unpack_uint()
+ self.packer.pack_uint(xid)
+ temp = self.unpacker.unpack_enum()
+ if temp != CALL:
+ return None # Not worthy of a reply
+ self.packer.pack_uint(REPLY)
+ temp = self.unpacker.unpack_uint()
+ if temp != RPCVERSION:
+ self.packer.pack_uint(MSG_DENIED)
+ self.packer.pack_uint(RPC_MISMATCH)
+ self.packer.pack_uint(RPCVERSION)
+ self.packer.pack_uint(RPCVERSION)
+ return self.packer.get_buf()
+ self.packer.pack_uint(MSG_ACCEPTED)
+ self.packer.pack_auth((AUTH_NULL, make_auth_null()))
+ prog = self.unpacker.unpack_uint()
+ if prog != self.prog:
+ self.packer.pack_uint(PROG_UNAVAIL)
+ return self.packer.get_buf()
+ vers = self.unpacker.unpack_uint()
+ if vers != self.vers:
+ self.packer.pack_uint(PROG_MISMATCH)
+ self.packer.pack_uint(self.vers)
+ self.packer.pack_uint(self.vers)
+ return self.packer.get_buf()
+ proc = self.unpacker.unpack_uint()
+ methname = 'handle_' + repr(proc)
+ try:
+ meth = getattr(self, methname)
+ except AttributeError:
+ self.packer.pack_uint(PROC_UNAVAIL)
+ return self.packer.get_buf()
+ cred = self.unpacker.unpack_auth()
+ verf = self.unpacker.unpack_auth()
+ try:
+ meth() # Unpack args, call turn_around(), pack reply
+ except (EOFError, GarbageArgs):
+ # Too few or too many arguments
+ self.packer.reset()
+ self.packer.pack_uint(xid)
+ self.packer.pack_uint(REPLY)
+ self.packer.pack_uint(MSG_ACCEPTED)
+ self.packer.pack_auth((AUTH_NULL, make_auth_null()))
+ self.packer.pack_uint(GARBAGE_ARGS)
+ return self.packer.get_buf()
+
+ def turn_around(self):
+ try:
+ self.unpacker.done()
+ except RuntimeError:
+ raise GarbageArgs
+ self.packer.pack_uint(SUCCESS)
+
+ def handle_0(self): # Handle NULL message
+ self.turn_around()
+
+ def makesocket(self):
+ # This MUST be overridden
+ raise RuntimeError, 'makesocket not defined'
+
+ def bindsocket(self):
+ # Override this to bind to a different port (e.g. reserved)
+ self.sock.bind((self.host, self.port))
+
+ def addpackers(self):
+ # Override this to use derived classes from Packer/Unpacker
+ self.packer = Packer()
+ self.unpacker = Unpacker('')
+
+
+class TCPServer(Server):
+
+ def makesocket(self):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.prot = IPPROTO_TCP
+
+ def loop(self):
+ self.sock.listen(0)
+ while 1:
+ self.session(self.sock.accept())
+
+ def session(self, connection):
+ sock, (host, port) = connection
+ while 1:
+ try:
+ call = recvrecord(sock)
+ except EOFError:
+ break
+ except socket.error, msg:
+ print 'socket error:', msg
+ break
+ reply = self.handle(call)
+ if reply is not None:
+ sendrecord(sock, reply)
+
+ def forkingloop(self):
+ # Like loop but uses forksession()
+ self.sock.listen(0)
+ while 1:
+ self.forksession(self.sock.accept())
+
+ def forksession(self, connection):
+ # Like session but forks off a subprocess
+ import os
+ # Wait for deceased children
+ try:
+ while 1:
+ pid, sts = os.waitpid(0, 1)
+ except os.error:
+ pass
+ pid = None
+ try:
+ pid = os.fork()
+ if pid: # Parent
+ connection[0].close()
+ return
+ # Child
+ self.session(connection)
+ finally:
+ # Make sure we don't fall through in the parent
+ if pid == 0:
+ os._exit(0)
+
+
+class UDPServer(Server):
+
+ def makesocket(self):
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.prot = IPPROTO_UDP
+
+ def loop(self):
+ while 1:
+ self.session()
+
+ def session(self):
+ call, host_port = self.sock.recvfrom(8192)
+ reply = self.handle(call)
+ if reply != None:
+ self.sock.sendto(reply, host_port)
+
+
+# Simple test program -- dump local portmapper status
+
+def test():
+ pmap = UDPPortMapperClient('')
+ list = pmap.Dump()
+ list.sort()
+ for prog, vers, prot, port in list:
+ print prog, vers,
+ if prot == IPPROTO_TCP: print 'tcp',
+ elif prot == IPPROTO_UDP: print 'udp',
+ else: print prot,
+ print port
+
+
+# Test program for broadcast operation -- dump everybody's portmapper status
+
+def testbcast():
+ import sys
+ if sys.argv[1:]:
+ bcastaddr = sys.argv[1]
+ else:
+ bcastaddr = '<broadcast>'
+ def rh(reply, fromaddr):
+ host, port = fromaddr
+ print host + '\t' + repr(reply)
+ pmap = BroadcastUDPPortMapperClient(bcastaddr)
+ pmap.set_reply_handler(rh)
+ pmap.set_timeout(5)
+ replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0))
+
+
+# Test program for server, with corresponding client
+# On machine A: python -c 'import rpc; rpc.testsvr()'
+# On machine B: python -c 'import rpc; rpc.testclt()' A
+# (A may be == B)
+
+def testsvr():
+ # Simple test class -- proc 1 doubles its string argument as reply
+ class S(UDPServer):
+ def handle_1(self):
+ arg = self.unpacker.unpack_string()
+ self.turn_around()
+ print 'RPC function 1 called, arg', repr(arg)
+ self.packer.pack_string(arg + arg)
+ #
+ s = S('', 0x20000000, 1, 0)
+ try:
+ s.unregister()
+ except RuntimeError, msg:
+ print 'RuntimeError:', msg, '(ignored)'
+ s.register()
+ print 'Service started...'
+ try:
+ s.loop()
+ finally:
+ s.unregister()
+ print 'Service interrupted.'
+
+
+def testclt():
+ import sys
+ if sys.argv[1:]: host = sys.argv[1]
+ else: host = ''
+ # Client for above server
+ class C(UDPClient):
+ def call_1(self, arg):
+ return self.make_call(1, arg, \
+ self.packer.pack_string, \
+ self.unpacker.unpack_string)
+ c = C(host, 0x20000000, 1)
+ print 'making call...'
+ reply = c.call_1('hello, world, ')
+ print 'call returned', repr(reply)
diff --git a/sys/src/cmd/python/Demo/rpc/test b/sys/src/cmd/python/Demo/rpc/test
new file mode 100755
index 000000000..ba220f24a
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/test
@@ -0,0 +1,24 @@
+: ${PYTHON=python}
+: ${SERVER=charon.cwi.nl}
+
+set -xe
+
+$PYTHON -c 'from rpc import test; test()'
+$PYTHON -c 'from rpc import test; test()' ${SERVER}
+
+$PYTHON -c 'from rpc import testsvr; testsvr()' &
+PID=$!
+sleep 2
+$PYTHON -c 'from rpc import testclt; testclt()'
+kill -2 $PID
+
+$PYTHON -c 'from mountclient import test; test()'
+$PYTHON -c 'from mountclient import test; test()' gatekeeper.dec.com
+
+$PYTHON -c 'from nfsclient import test; test()'
+$PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com
+$PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com /archive
+
+$PYTHON -c 'from rnusersclient import test; test()' ''
+
+$PYTHON -c 'from rpc import testbcast; testbcast()'
diff --git a/sys/src/cmd/python/Demo/rpc/xdr.py b/sys/src/cmd/python/Demo/rpc/xdr.py
new file mode 100644
index 000000000..df5cbafd0
--- /dev/null
+++ b/sys/src/cmd/python/Demo/rpc/xdr.py
@@ -0,0 +1,200 @@
+# Implement (a subset of) Sun XDR -- RFC1014.
+
+
+try:
+ import struct
+except ImportError:
+ struct = None
+
+
+Long = type(0L)
+
+
+class Packer:
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.buf = ''
+
+ def get_buf(self):
+ return self.buf
+
+ def pack_uint(self, x):
+ self.buf = self.buf + \
+ (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
+ chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
+ if struct and struct.pack('l', 1) == '\0\0\0\1':
+ def pack_uint(self, x):
+ if type(x) == Long:
+ x = int((x + 0x80000000L) % 0x100000000L \
+ - 0x80000000L)
+ self.buf = self.buf + struct.pack('l', x)
+
+ pack_int = pack_uint
+
+ pack_enum = pack_int
+
+ def pack_bool(self, x):
+ if x: self.buf = self.buf + '\0\0\0\1'
+ else: self.buf = self.buf + '\0\0\0\0'
+
+ def pack_uhyper(self, x):
+ self.pack_uint(int(x>>32 & 0xffffffff))
+ self.pack_uint(int(x & 0xffffffff))
+
+ pack_hyper = pack_uhyper
+
+ def pack_float(self, x):
+ # XXX
+ self.buf = self.buf + struct.pack('f', x)
+
+ def pack_double(self, x):
+ # XXX
+ self.buf = self.buf + struct.pack('d', x)
+
+ def pack_fstring(self, n, s):
+ if n < 0:
+ raise ValueError, 'fstring size must be nonnegative'
+ n = ((n+3)/4)*4
+ data = s[:n]
+ data = data + (n - len(data)) * '\0'
+ self.buf = self.buf + data
+
+ pack_fopaque = pack_fstring
+
+ def pack_string(self, s):
+ n = len(s)
+ self.pack_uint(n)
+ self.pack_fstring(n, s)
+
+ pack_opaque = pack_string
+
+ def pack_list(self, list, pack_item):
+ for item in list:
+ self.pack_uint(1)
+ pack_item(item)
+ self.pack_uint(0)
+
+ def pack_farray(self, n, list, pack_item):
+ if len(list) <> n:
+ raise ValueError, 'wrong array size'
+ for item in list:
+ pack_item(item)
+
+ def pack_array(self, list, pack_item):
+ n = len(list)
+ self.pack_uint(n)
+ self.pack_farray(n, list, pack_item)
+
+
+class Unpacker:
+
+ def __init__(self, data):
+ self.reset(data)
+
+ def reset(self, data):
+ self.buf = data
+ self.pos = 0
+
+ def done(self):
+ if self.pos < len(self.buf):
+ raise RuntimeError, 'unextracted data remains'
+
+ def unpack_uint(self):
+ i = self.pos
+ self.pos = j = i+4
+ data = self.buf[i:j]
+ if len(data) < 4:
+ raise EOFError
+ x = long(ord(data[0]))<<24 | ord(data[1])<<16 | \
+ ord(data[2])<<8 | ord(data[3])
+ # Return a Python long only if the value is not representable
+ # as a nonnegative Python int
+ if x < 0x80000000L: x = int(x)
+ return x
+ if struct and struct.unpack('l', '\0\0\0\1') == 1:
+ def unpack_uint(self):
+ i = self.pos
+ self.pos = j = i+4
+ data = self.buf[i:j]
+ if len(data) < 4:
+ raise EOFError
+ return struct.unpack('l', data)
+
+ def unpack_int(self):
+ x = self.unpack_uint()
+ if x >= 0x80000000L: x = x - 0x100000000L
+ return int(x)
+
+ unpack_enum = unpack_int
+
+ unpack_bool = unpack_int
+
+ def unpack_uhyper(self):
+ hi = self.unpack_uint()
+ lo = self.unpack_uint()
+ return long(hi)<<32 | lo
+
+ def unpack_hyper(self):
+ x = self.unpack_uhyper()
+ if x >= 0x8000000000000000L: x = x - 0x10000000000000000L
+ return x
+
+ def unpack_float(self):
+ # XXX
+ i = self.pos
+ self.pos = j = i+4
+ data = self.buf[i:j]
+ if len(data) < 4:
+ raise EOFError
+ return struct.unpack('f', data)[0]
+
+ def unpack_double(self):
+ # XXX
+ i = self.pos
+ self.pos = j = i+8
+ data = self.buf[i:j]
+ if len(data) < 8:
+ raise EOFError
+ return struct.unpack('d', data)[0]
+
+ def unpack_fstring(self, n):
+ if n < 0:
+ raise ValueError, 'fstring size must be nonnegative'
+ i = self.pos
+ j = i + (n+3)/4*4
+ if j > len(self.buf):
+ raise EOFError
+ self.pos = j
+ return self.buf[i:i+n]
+
+ unpack_fopaque = unpack_fstring
+
+ def unpack_string(self):
+ n = self.unpack_uint()
+ return self.unpack_fstring(n)
+
+ unpack_opaque = unpack_string
+
+ def unpack_list(self, unpack_item):
+ list = []
+ while 1:
+ x = self.unpack_uint()
+ if x == 0: break
+ if x <> 1:
+ raise RuntimeError, '0 or 1 expected, got %r' % (x, )
+ item = unpack_item()
+ list.append(item)
+ return list
+
+ def unpack_farray(self, n, unpack_item):
+ list = []
+ for i in range(n):
+ list.append(unpack_item())
+ return list
+
+ def unpack_array(self, unpack_item):
+ n = self.unpack_uint()
+ return self.unpack_farray(n, unpack_item)