mountclient.py revision 9e80d6f12512803532aed39a0335e6346b77abf4
1# 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
11import rpc
12from rpc import Packer, Unpacker, TCPClient, UDPClient
13
14
15# Program number and version for the mount protocol
16MOUNTPROG = 100005
17MOUNTVERS = 1
18
19# Size of the 'fhandle' opaque structure
20FHSIZE = 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
77	# This method is called by Client.__init__ to initialize
78	# self.packer and self.unpacker
79	def addpackers(self):
80		self.packer = MountPacker()
81		self.unpacker = MountUnpacker('')
82
83	# This method is called by Client.__init__ to bind the socket
84	# 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
89		try:
90			uid = os.getuid()
91		except AttributeError:
92			uid = 1
93		if uid == 0:
94			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
100	# authentication object for a call to procedure 'proc'.
101	def mkcred(self):
102		if self.cred == None:
103			self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default()
104		return self.cred
105
106	# The methods Mnt, Dump etc. each implement one Remote
107	# 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.
135
136	def Mnt(self, directory):
137		return self.make_call(1, directory, \
138			self.packer.pack_string, \
139			self.unpacker.unpack_fhstatus)
140
141	def Dump(self):
142		return self.make_call(2, None, \
143			None, self.unpacker.unpack_mountlist)
144
145	def Umnt(self, directory):
146		return self.make_call(3, directory, \
147			self.packer.pack_string, None)
148
149	def Umntall(self):
150		return self.make_call(4, None, None, None)
151
152	def Export(self):
153		return self.make_call(5, None, \
154			None, self.unpacker.unpack_exportlist)
155
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
164	def __init__(self, host):
165		TCPClient.__init__(self, host, MOUNTPROG, MOUNTVERS)
166
167
168class UDPMountClient(PartialMountClient, UDPClient):
169
170	def __init__(self, host):
171		UDPClient.__init__(self, host, MOUNTPROG, MOUNTVERS)
172
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
176# 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.
179
180def test():
181	import sys
182	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
190	if sys.argv[1:]: host = sys.argv[1]
191	else: host = ''
192	mcl = C(host)
193	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])
202