blob: ff66b3d6ccf54b1e0d1cca3590847e5c2b4ade10 [file] [log] [blame]
Guido van Rossume3cafbe1992-12-14 23:25:04 +00001# Mount RPC client -- RFC 1094 (NFS), Appendix A
2
3# This module demonstrates how to write your own RPC client in Python.
4# Since there is no RPC compiler for Python (yet), you must first
5# create classes derived from Packer and Unpacker to handle the data
6# types for the server you want to interface to. You then write the
7# client class. If you want to support both the TCP and the UDP
8# version of a protocol, use multiple inheritance as shown below.
9
10
Guido van Rossum20f99601992-12-15 20:53:17 +000011import rpc
Guido van Rossume3cafbe1992-12-14 23:25:04 +000012from rpc import Packer, Unpacker, TCPClient, UDPClient
13
Guido van Rossumfd92ac81992-12-20 14:57:17 +000014
15# Program number and version for the mount protocol
Guido van Rossume3cafbe1992-12-14 23:25:04 +000016MOUNTPROG = 100005
17MOUNTVERS = 1
18
Guido van Rossumfd92ac81992-12-20 14:57:17 +000019# Size of the 'fhandle' opaque structure
Guido van Rossume3cafbe1992-12-14 23:25:04 +000020FHSIZE = 32
21
22
23# Packer derived class for Mount protocol clients.
24# The only thing we need to pack beyond basic types is an 'fhandle'
25
26class MountPacker(Packer):
27
28 def pack_fhandle(self, fhandle):
29 self.pack_fopaque(FHSIZE, fhandle)
30
31
32# Unpacker derived class for Mount protocol clients.
33# The important types we need to unpack are fhandle, fhstatus,
34# mountlist and exportlist; mountstruct, exportstruct and groups are
35# used to unpack components of mountlist and exportlist and the
36# corresponding functions are passed as function argument to the
37# generic unpack_list function.
38
39class MountUnpacker(Unpacker):
40
41 def unpack_fhandle(self):
42 return self.unpack_fopaque(FHSIZE)
43
44 def unpack_fhstatus(self):
45 status = self.unpack_uint()
46 if status == 0:
47 fh = self.unpack_fhandle()
48 else:
49 fh = None
50 return status, fh
51
52 def unpack_mountlist(self):
53 return self.unpack_list(self.unpack_mountstruct)
54
55 def unpack_mountstruct(self):
56 hostname = self.unpack_string()
57 directory = self.unpack_string()
58 return (hostname, directory)
59
60 def unpack_exportlist(self):
61 return self.unpack_list(self.unpack_exportstruct)
62
63 def unpack_exportstruct(self):
64 filesys = self.unpack_string()
65 groups = self.unpack_groups()
66 return (filesys, groups)
67
68 def unpack_groups(self):
69 return self.unpack_list(self.unpack_string)
70
71
72# These are the procedures specific to the Mount client class.
73# Think of this as a derived class of either TCPClient or UDPClient.
74
75class PartialMountClient:
76
Guido van Rossum9e80d6f1993-12-17 14:32:26 +000077 # This method is called by Client.__init__ to initialize
Guido van Rossume3cafbe1992-12-14 23:25:04 +000078 # self.packer and self.unpacker
79 def addpackers(self):
Guido van Rossum9e80d6f1993-12-17 14:32:26 +000080 self.packer = MountPacker()
81 self.unpacker = MountUnpacker('')
Guido van Rossume3cafbe1992-12-14 23:25:04 +000082
Guido van Rossum9e80d6f1993-12-17 14:32:26 +000083 # This method is called by Client.__init__ to bind the socket
Guido van Rossumfd92ac81992-12-20 14:57:17 +000084 # to a particular network interface and port. We use the
85 # default network interface, but if we're running as root,
86 # we want to bind to a reserved port
87 def bindsocket(self):
88 import os
Guido van Rossum73065381992-12-21 14:32:28 +000089 try:
90 uid = os.getuid()
91 except AttributeError:
92 uid = 1
93 if uid == 0:
Guido van Rossumfd92ac81992-12-20 14:57:17 +000094 port = rpc.bindresvport(self.sock, '')
95 # 'port' is not used
96 else:
97 self.sock.bind(('', 0))
98
99 # This function is called to cough up a suitable
Guido van Rossum20f99601992-12-15 20:53:17 +0000100 # authentication object for a call to procedure 'proc'.
Guido van Rossumfd92ac81992-12-20 14:57:17 +0000101 def mkcred(self):
Guido van Rossum20f99601992-12-15 20:53:17 +0000102 if self.cred == None:
103 self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default()
104 return self.cred
105
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000106 # The methods Mnt, Dump etc. each implement one Remote
Guido van Rossum73065381992-12-21 14:32:28 +0000107 # Procedure Call. This is done by calling self.make_call()
108 # with as arguments:
109 #
110 # - the procedure number
111 # - the arguments (or None)
112 # - the "packer" function for the arguments (or None)
113 # - the "unpacker" function for the return value (or None)
114 #
115 # The packer and unpacker function, if not None, *must* be
116 # methods of self.packer and self.unpacker, respectively.
117 # A value of None means that there are no arguments or is no
118 # return value, respectively.
119 #
120 # The return value from make_call() is the return value from
121 # the remote procedure call, as unpacked by the "unpacker"
122 # function, or None if the unpacker function is None.
123 #
124 # (Even if you expect a result of None, you should still
125 # return the return value from make_call(), since this may be
126 # needed by a broadcasting version of the class.)
127 #
128 # If the call fails, make_call() raises an exception
129 # (this includes time-outs and invalid results).
130 #
131 # Note that (at least with the UDP protocol) there is no
132 # guarantee that a call is executed at most once. When you do
133 # get a reply, you know it has been executed at least once;
134 # when you don't get a reply, you know nothing.
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000135
136 def Mnt(self, directory):
Guido van Rossum73065381992-12-21 14:32:28 +0000137 return self.make_call(1, directory, \
138 self.packer.pack_string, \
139 self.unpacker.unpack_fhstatus)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000140
141 def Dump(self):
Guido van Rossum73065381992-12-21 14:32:28 +0000142 return self.make_call(2, None, \
143 None, self.unpacker.unpack_mountlist)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000144
145 def Umnt(self, directory):
Guido van Rossum73065381992-12-21 14:32:28 +0000146 return self.make_call(3, directory, \
147 self.packer.pack_string, None)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000148
149 def Umntall(self):
Guido van Rossum73065381992-12-21 14:32:28 +0000150 return self.make_call(4, None, None, None)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000151
152 def Export(self):
Guido van Rossum73065381992-12-21 14:32:28 +0000153 return self.make_call(5, None, \
154 None, self.unpacker.unpack_exportlist)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000155
156
157# We turn the partial Mount client into a full one for either protocol
158# by use of multiple inheritance. (In general, when class C has base
159# classes B1...Bn, if x is an instance of class C, methods of x are
160# searched first in C, then in B1, then in B2, ..., finally in Bn.)
161
162class TCPMountClient(PartialMountClient, TCPClient):
163
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000164 def __init__(self, host):
165 TCPClient.__init__(self, host, MOUNTPROG, MOUNTVERS)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000166
167
168class UDPMountClient(PartialMountClient, UDPClient):
169
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000170 def __init__(self, host):
171 UDPClient.__init__(self, host, MOUNTPROG, MOUNTVERS)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000172
173
174# A little test program for the Mount client. This takes a host as
175# command line argument (default the local machine), prints its export
Guido van Rossumfd92ac81992-12-20 14:57:17 +0000176# list, and attempts to mount and unmount each exported files system.
177# An optional first argument of -t or -u specifies the protocol to use
178# (TCP or UDP), default is UDP.
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000179
180def test():
181 import sys
Guido van Rossumfd92ac81992-12-20 14:57:17 +0000182 if sys.argv[1:] and sys.argv[1] == '-t':
183 C = TCPMountClient
184 del sys.argv[1]
185 elif sys.argv[1:] and sys.argv[1] == '-u':
186 C = UDPMountClient
187 del sys.argv[1]
188 else:
189 C = UDPMountClient
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000190 if sys.argv[1:]: host = sys.argv[1]
191 else: host = ''
Guido van Rossum9e80d6f1993-12-17 14:32:26 +0000192 mcl = C(host)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000193 list = mcl.Export()
194 for item in list:
195 print item
196 try:
197 mcl.Mnt(item[0])
198 except:
199 print 'Sorry'
200 continue
201 mcl.Umnt(item[0])