1*9c5db199SXin Li# Copyright 2018 The Chromium OS Authors. All rights reserved. 2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 3*9c5db199SXin Li# found in the LICENSE file. 4*9c5db199SXin Li 5*9c5db199SXin Liimport logging 6*9c5db199SXin Liimport os 7*9c5db199SXin Liimport sys 8*9c5db199SXin Li 9*9c5db199SXin Liimport dbus 10*9c5db199SXin Li 11*9c5db199SXin Lifrom autotest_lib.client.cros import upstart 12*9c5db199SXin Li 13*9c5db199SXin Lidef _proto_to_blob(proto): 14*9c5db199SXin Li return dbus.ByteArray(proto.SerializeToString()) 15*9c5db199SXin Li 16*9c5db199SXin Liclass SmbProvider(object): 17*9c5db199SXin Li """ 18*9c5db199SXin Li Wrapper for D-Bus calls to SmbProvider Daemon 19*9c5db199SXin Li 20*9c5db199SXin Li The SmbProvider daemon handles calling the libsmbclient to communicate with 21*9c5db199SXin Li an SMB server. This class is a wrapper to the D-Bus interface to the daemon. 22*9c5db199SXin Li 23*9c5db199SXin Li """ 24*9c5db199SXin Li 25*9c5db199SXin Li _DBUS_SERVICE_NAME = "org.chromium.SmbProvider" 26*9c5db199SXin Li _DBUS_SERVICE_PATH = "/org/chromium/SmbProvider" 27*9c5db199SXin Li _DBUS_INTERFACE_NAME = "org.chromium.SmbProvider" 28*9c5db199SXin Li 29*9c5db199SXin Li # Default timeout in seconds for D-Bus calls. 30*9c5db199SXin Li _DEFAULT_TIMEOUT = 120 31*9c5db199SXin Li 32*9c5db199SXin Li # Chronos user ID. 33*9c5db199SXin Li _CHRONOS_UID = 1000 34*9c5db199SXin Li 35*9c5db199SXin Li def __init__(self, bus_loop, proto_binding_location): 36*9c5db199SXin Li """ 37*9c5db199SXin Li Constructor. 38*9c5db199SXin Li 39*9c5db199SXin Li Creates and D-Bus connection to smbproviderd. 40*9c5db199SXin Li 41*9c5db199SXin Li @param bus_loop: Glib main loop object 42*9c5db199SXin Li @param proto_binding_location: The location of generated python bindings 43*9c5db199SXin Li for smbprovider protobufs. 44*9c5db199SXin Li 45*9c5db199SXin Li """ 46*9c5db199SXin Li 47*9c5db199SXin Li sys.path.append(proto_binding_location) 48*9c5db199SXin Li self._bus_loop = bus_loop 49*9c5db199SXin Li self.restart() 50*9c5db199SXin Li 51*9c5db199SXin Li def restart(self): 52*9c5db199SXin Li """ 53*9c5db199SXin Li Restarts smbproviderd and rebinds to D-Bus interface. 54*9c5db199SXin Li 55*9c5db199SXin Li """ 56*9c5db199SXin Li 57*9c5db199SXin Li logging.info('restarting smbproviderd') 58*9c5db199SXin Li upstart.restart_job('smbproviderd') 59*9c5db199SXin Li 60*9c5db199SXin Li try: 61*9c5db199SXin Li # Get the interface as Chronos since only they are allowed to send 62*9c5db199SXin Li # D-Bus messages to smbproviderd. 63*9c5db199SXin Li os.setresuid(self._CHRONOS_UID, self._CHRONOS_UID, 0) 64*9c5db199SXin Li 65*9c5db199SXin Li bus = dbus.SystemBus(self._bus_loop) 66*9c5db199SXin Li proxy = bus.get_object(self._DBUS_SERVICE_NAME, 67*9c5db199SXin Li self._DBUS_SERVICE_PATH) 68*9c5db199SXin Li self._smbproviderd = dbus.Interface(proxy, 69*9c5db199SXin Li self._DBUS_INTERFACE_NAME) 70*9c5db199SXin Li 71*9c5db199SXin Li finally: 72*9c5db199SXin Li os.setresuid(0, 0, 0) 73*9c5db199SXin Li 74*9c5db199SXin Li def stop(self): 75*9c5db199SXin Li """ 76*9c5db199SXin Li Stops smbproviderd. 77*9c5db199SXin Li 78*9c5db199SXin Li """ 79*9c5db199SXin Li 80*9c5db199SXin Li logging.info('stopping smbproviderd') 81*9c5db199SXin Li 82*9c5db199SXin Li try: 83*9c5db199SXin Li upstart.stop_job('smbproviderd') 84*9c5db199SXin Li 85*9c5db199SXin Li finally: 86*9c5db199SXin Li self._smbproviderd = None 87*9c5db199SXin Li 88*9c5db199SXin Li def mount(self, mount_path, workgroup, username, password): 89*9c5db199SXin Li """ 90*9c5db199SXin Li Mounts a share. 91*9c5db199SXin Li 92*9c5db199SXin Li @param mount_path: Path of the share to mount. 93*9c5db199SXin Li @param workgroup: Workgroup for the mount. 94*9c5db199SXin Li @param username: Username for the mount. 95*9c5db199SXin Li @param password: Password for the mount. 96*9c5db199SXin Li 97*9c5db199SXin Li @return A tuple with the ErrorType and the mount id returned the D-Bus 98*9c5db199SXin Li call. 99*9c5db199SXin Li 100*9c5db199SXin Li """ 101*9c5db199SXin Li 102*9c5db199SXin Li logging.info("Mounting: %s", mount_path) 103*9c5db199SXin Li 104*9c5db199SXin Li from directory_entry_pb2 import MountOptionsProto 105*9c5db199SXin Li from directory_entry_pb2 import MountConfigProto 106*9c5db199SXin Li 107*9c5db199SXin Li proto = MountOptionsProto() 108*9c5db199SXin Li proto.path = mount_path 109*9c5db199SXin Li proto.workgroup = workgroup 110*9c5db199SXin Li proto.username = username 111*9c5db199SXin Li proto.mount_config.enable_ntlm = True 112*9c5db199SXin Li 113*9c5db199SXin Li with self.DataFd(password) as password_fd: 114*9c5db199SXin Li return self._smbproviderd.Mount(_proto_to_blob(proto), 115*9c5db199SXin Li dbus.types.UnixFd(password_fd), 116*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 117*9c5db199SXin Li byte_arrays=True) 118*9c5db199SXin Li 119*9c5db199SXin Li def unmount(self, mount_id): 120*9c5db199SXin Li """ 121*9c5db199SXin Li Unmounts a share. 122*9c5db199SXin Li 123*9c5db199SXin Li @param mount_id: Mount ID to be umounted. 124*9c5db199SXin Li 125*9c5db199SXin Li @return: ErrorType from the returned D-Bus call. 126*9c5db199SXin Li 127*9c5db199SXin Li """ 128*9c5db199SXin Li 129*9c5db199SXin Li logging.info("Unmounting: %s", mount_id) 130*9c5db199SXin Li 131*9c5db199SXin Li from directory_entry_pb2 import UnmountOptionsProto 132*9c5db199SXin Li 133*9c5db199SXin Li proto = UnmountOptionsProto() 134*9c5db199SXin Li proto.mount_id = mount_id 135*9c5db199SXin Li 136*9c5db199SXin Li return self._smbproviderd.Unmount(_proto_to_blob(proto)) 137*9c5db199SXin Li 138*9c5db199SXin Li def create_directory(self, mount_id, directory_path, recursive): 139*9c5db199SXin Li """ 140*9c5db199SXin Li Creates a directory. 141*9c5db199SXin Li 142*9c5db199SXin Li @param mount_id: Mount ID corresponsding to the share. 143*9c5db199SXin Li @param directory_path: Path of the directory to read. 144*9c5db199SXin Li @param recursive: Boolean to indicate whether directories should be 145*9c5db199SXin Li created recursively. 146*9c5db199SXin Li 147*9c5db199SXin Li @return: ErrorType from the returned D-Bus call. 148*9c5db199SXin Li 149*9c5db199SXin Li """ 150*9c5db199SXin Li 151*9c5db199SXin Li logging.info("Creating directory: %s", directory_path) 152*9c5db199SXin Li 153*9c5db199SXin Li from directory_entry_pb2 import CreateDirectoryOptionsProto 154*9c5db199SXin Li from directory_entry_pb2 import ERROR_OK 155*9c5db199SXin Li 156*9c5db199SXin Li proto = CreateDirectoryOptionsProto() 157*9c5db199SXin Li proto.mount_id = mount_id 158*9c5db199SXin Li proto.directory_path = directory_path 159*9c5db199SXin Li proto.recursive = recursive 160*9c5db199SXin Li 161*9c5db199SXin Li return self._smbproviderd.CreateDirectory( 162*9c5db199SXin Li _proto_to_blob(proto), 163*9c5db199SXin Li timout=self._DEFAULT_TIMEOUT, 164*9c5db199SXin Li byte_arrays=True) 165*9c5db199SXin Li 166*9c5db199SXin Li 167*9c5db199SXin Li def read_directory(self, mount_id, directory_path): 168*9c5db199SXin Li """ 169*9c5db199SXin Li Reads a directory. 170*9c5db199SXin Li 171*9c5db199SXin Li @param mount_id: Mount ID corresponding to the share. 172*9c5db199SXin Li @param directory_path: Path of the directory to read. 173*9c5db199SXin Li 174*9c5db199SXin Li @return A tuple with the ErrorType and the DirectoryEntryListProto blob 175*9c5db199SXin Li string returned by the D-Bus call. 176*9c5db199SXin Li 177*9c5db199SXin Li """ 178*9c5db199SXin Li 179*9c5db199SXin Li logging.info("Reading directory: %s", directory_path) 180*9c5db199SXin Li 181*9c5db199SXin Li from directory_entry_pb2 import ReadDirectoryOptionsProto 182*9c5db199SXin Li from directory_entry_pb2 import DirectoryEntryListProto 183*9c5db199SXin Li from directory_entry_pb2 import ERROR_OK 184*9c5db199SXin Li 185*9c5db199SXin Li proto = ReadDirectoryOptionsProto() 186*9c5db199SXin Li proto.mount_id = mount_id 187*9c5db199SXin Li proto.directory_path = directory_path 188*9c5db199SXin Li 189*9c5db199SXin Li error, entries_blob = self._smbproviderd.ReadDirectory( 190*9c5db199SXin Li _proto_to_blob(proto), 191*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 192*9c5db199SXin Li byte_arrays=True) 193*9c5db199SXin Li 194*9c5db199SXin Li entries = DirectoryEntryListProto() 195*9c5db199SXin Li if error == ERROR_OK: 196*9c5db199SXin Li entries.ParseFromString(entries_blob) 197*9c5db199SXin Li 198*9c5db199SXin Li return error, entries 199*9c5db199SXin Li 200*9c5db199SXin Li def get_metadata(self, mount_id, entry_path): 201*9c5db199SXin Li """ 202*9c5db199SXin Li Gets metadata for an entry. 203*9c5db199SXin Li 204*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 205*9c5db199SXin Li @param entry_path: Path of the entry. 206*9c5db199SXin Li 207*9c5db199SXin Li @return A tuple with the ErrorType and the GetMetadataEntryOptionsProto 208*9c5db199SXin Li blob string returned by the D-Bus call. 209*9c5db199SXin Li 210*9c5db199SXin Li """ 211*9c5db199SXin Li 212*9c5db199SXin Li logging.info("Getting metadata for %s", entry_path) 213*9c5db199SXin Li 214*9c5db199SXin Li from directory_entry_pb2 import GetMetadataEntryOptionsProto 215*9c5db199SXin Li from directory_entry_pb2 import DirectoryEntryProto 216*9c5db199SXin Li from directory_entry_pb2 import ERROR_OK 217*9c5db199SXin Li 218*9c5db199SXin Li proto = GetMetadataEntryOptionsProto() 219*9c5db199SXin Li proto.mount_id = mount_id 220*9c5db199SXin Li proto.entry_path = entry_path 221*9c5db199SXin Li 222*9c5db199SXin Li error, entry_blob = self._smbproviderd.GetMetadataEntry( 223*9c5db199SXin Li _proto_to_blob(proto), 224*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 225*9c5db199SXin Li byte_arrays=True) 226*9c5db199SXin Li 227*9c5db199SXin Li entry = DirectoryEntryProto() 228*9c5db199SXin Li if error == ERROR_OK: 229*9c5db199SXin Li entry.ParseFromString(entry_blob) 230*9c5db199SXin Li 231*9c5db199SXin Li return error, entry 232*9c5db199SXin Li 233*9c5db199SXin Li def open_file(self, mount_id, file_path, writeable): 234*9c5db199SXin Li """ 235*9c5db199SXin Li Opens a file. 236*9c5db199SXin Li 237*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 238*9c5db199SXin Li @param file_path: Path of the file to be opened. 239*9c5db199SXin Li @param writeable: Whether the file should be opened with write access. 240*9c5db199SXin Li 241*9c5db199SXin Li @return A tuple with the ErrorType and the File ID of the opened file. 242*9c5db199SXin Li 243*9c5db199SXin Li """ 244*9c5db199SXin Li 245*9c5db199SXin Li logging.info("Opening file: %s", file_path) 246*9c5db199SXin Li 247*9c5db199SXin Li from directory_entry_pb2 import OpenFileOptionsProto 248*9c5db199SXin Li 249*9c5db199SXin Li proto = OpenFileOptionsProto() 250*9c5db199SXin Li proto.mount_id = mount_id 251*9c5db199SXin Li proto.file_path = file_path 252*9c5db199SXin Li proto.writeable = writeable 253*9c5db199SXin Li 254*9c5db199SXin Li return self._smbproviderd.OpenFile(_proto_to_blob(proto), 255*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 256*9c5db199SXin Li byte_arrays=True) 257*9c5db199SXin Li 258*9c5db199SXin Li def close_file(self, mount_id, file_id): 259*9c5db199SXin Li """ 260*9c5db199SXin Li Closes a file. 261*9c5db199SXin Li 262*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 263*9c5db199SXin Li @param file_id: ID of the file to be closed. 264*9c5db199SXin Li 265*9c5db199SXin Li @return ErrorType returned from the D-Bus call. 266*9c5db199SXin Li 267*9c5db199SXin Li """ 268*9c5db199SXin Li 269*9c5db199SXin Li logging.info("Closing file: %s", file_id) 270*9c5db199SXin Li 271*9c5db199SXin Li from directory_entry_pb2 import CloseFileOptionsProto 272*9c5db199SXin Li 273*9c5db199SXin Li proto = CloseFileOptionsProto() 274*9c5db199SXin Li proto.mount_id = mount_id 275*9c5db199SXin Li proto.file_id = file_id 276*9c5db199SXin Li 277*9c5db199SXin Li return self._smbproviderd.CloseFile(_proto_to_blob(proto), 278*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 279*9c5db199SXin Li byte_arrays=True) 280*9c5db199SXin Li 281*9c5db199SXin Li def read_file(self, mount_id, file_id, offset, length): 282*9c5db199SXin Li """ 283*9c5db199SXin Li Reads a file. 284*9c5db199SXin Li 285*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 286*9c5db199SXin Li @param file_id: ID of the file to be read. 287*9c5db199SXin Li @param offset: Offset to start reading. 288*9c5db199SXin Li @param length: Length in bytes to read. 289*9c5db199SXin Li 290*9c5db199SXin Li @return A tuple with ErrorType and and a buffer containing the data 291*9c5db199SXin Li read. 292*9c5db199SXin Li 293*9c5db199SXin Li """ 294*9c5db199SXin Li 295*9c5db199SXin Li logging.info("Reading file: %s", file_id) 296*9c5db199SXin Li 297*9c5db199SXin Li from directory_entry_pb2 import ReadFileOptionsProto 298*9c5db199SXin Li from directory_entry_pb2 import ERROR_OK 299*9c5db199SXin Li 300*9c5db199SXin Li proto = ReadFileOptionsProto() 301*9c5db199SXin Li proto.mount_id = mount_id 302*9c5db199SXin Li proto.file_id = file_id 303*9c5db199SXin Li proto.offset = offset 304*9c5db199SXin Li proto.length = length 305*9c5db199SXin Li 306*9c5db199SXin Li error, fd = self._smbproviderd.ReadFile(_proto_to_blob(proto), 307*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 308*9c5db199SXin Li byte_arrays=True) 309*9c5db199SXin Li 310*9c5db199SXin Li data = '' 311*9c5db199SXin Li if error == ERROR_OK: 312*9c5db199SXin Li data = os.read(fd.take(), length) 313*9c5db199SXin Li 314*9c5db199SXin Li return error, data 315*9c5db199SXin Li 316*9c5db199SXin Li def create_file(self, mount_id, file_path): 317*9c5db199SXin Li """ 318*9c5db199SXin Li Creates a file. 319*9c5db199SXin Li 320*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 321*9c5db199SXin Li @param file_path: Path of the file to be created. 322*9c5db199SXin Li 323*9c5db199SXin Li @return ErrorType returned from the D-Bus call. 324*9c5db199SXin Li 325*9c5db199SXin Li """ 326*9c5db199SXin Li 327*9c5db199SXin Li logging.info("Creating file: %s", file_path) 328*9c5db199SXin Li 329*9c5db199SXin Li from directory_entry_pb2 import CreateFileOptionsProto 330*9c5db199SXin Li 331*9c5db199SXin Li proto = CreateFileOptionsProto() 332*9c5db199SXin Li proto.mount_id = mount_id 333*9c5db199SXin Li proto.file_path = file_path 334*9c5db199SXin Li 335*9c5db199SXin Li return self._smbproviderd.CreateFile(_proto_to_blob(proto), 336*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 337*9c5db199SXin Li byte_arrays=True) 338*9c5db199SXin Li 339*9c5db199SXin Li def delete_entry(self, mount_id, entry_path, recursive): 340*9c5db199SXin Li """ 341*9c5db199SXin Li Deletes an entry. 342*9c5db199SXin Li 343*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 344*9c5db199SXin Li @param entry_path: Path of the entry to be deleted. 345*9c5db199SXin Li @param recursive: Boolean indicating whether the delete should be 346*9c5db199SXin Li recursive for directories. 347*9c5db199SXin Li 348*9c5db199SXin Li @return ErrorType returned from the D-Bus call. 349*9c5db199SXin Li 350*9c5db199SXin Li """ 351*9c5db199SXin Li 352*9c5db199SXin Li logging.info("Deleting entry: %s", entry_path) 353*9c5db199SXin Li 354*9c5db199SXin Li from directory_entry_pb2 import DeleteEntryOptionsProto 355*9c5db199SXin Li 356*9c5db199SXin Li proto = DeleteEntryOptionsProto() 357*9c5db199SXin Li proto.mount_id = mount_id 358*9c5db199SXin Li proto.entry_path = entry_path 359*9c5db199SXin Li proto.recursive = recursive 360*9c5db199SXin Li 361*9c5db199SXin Li return self._smbproviderd.DeleteEntry(_proto_to_blob(proto), 362*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 363*9c5db199SXin Li byte_arrays=True) 364*9c5db199SXin Li 365*9c5db199SXin Li def move_entry(self, mount_id, source_path, target_path): 366*9c5db199SXin Li """ 367*9c5db199SXin Li Moves an entry from source to target destination. 368*9c5db199SXin Li 369*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 370*9c5db199SXin Li @param source_path: Path of the entry to be moved. 371*9c5db199SXin Li @param target_path: Path of where the entry will be moved to. Target 372*9c5db199SXin Li path must be a non-existent path. 373*9c5db199SXin Li 374*9c5db199SXin Li @return ErrorType returned from the D-Bus call. 375*9c5db199SXin Li 376*9c5db199SXin Li """ 377*9c5db199SXin Li 378*9c5db199SXin Li logging.info("Moving file to: %s", target_path) 379*9c5db199SXin Li 380*9c5db199SXin Li from directory_entry_pb2 import MoveEntryOptionsProto 381*9c5db199SXin Li 382*9c5db199SXin Li proto = MoveEntryOptionsProto() 383*9c5db199SXin Li proto.mount_id = mount_id 384*9c5db199SXin Li proto.source_path = source_path 385*9c5db199SXin Li proto.target_path = target_path 386*9c5db199SXin Li 387*9c5db199SXin Li return self._smbproviderd.MoveEntry(_proto_to_blob(proto), 388*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 389*9c5db199SXin Li byte_arrays=True) 390*9c5db199SXin Li 391*9c5db199SXin Li def truncate(self, mount_id, file_path, length): 392*9c5db199SXin Li """ 393*9c5db199SXin Li Truncates a file. 394*9c5db199SXin Li 395*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 396*9c5db199SXin Li @param file_path: Path of the file to be truncated. 397*9c5db199SXin Li @param length: The new size of the file in bytes. 398*9c5db199SXin Li 399*9c5db199SXin Li @return ErrorType returned from the D-Bus call. 400*9c5db199SXin Li 401*9c5db199SXin Li """ 402*9c5db199SXin Li 403*9c5db199SXin Li logging.info("Truncating file: %s", file_path) 404*9c5db199SXin Li 405*9c5db199SXin Li from directory_entry_pb2 import TruncateOptionsProto 406*9c5db199SXin Li 407*9c5db199SXin Li proto = TruncateOptionsProto() 408*9c5db199SXin Li proto.mount_id = mount_id 409*9c5db199SXin Li proto.file_path = file_path 410*9c5db199SXin Li proto.length = length 411*9c5db199SXin Li 412*9c5db199SXin Li return self._smbproviderd.Truncate(_proto_to_blob(proto), 413*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 414*9c5db199SXin Li byte_arrays=True) 415*9c5db199SXin Li 416*9c5db199SXin Li def write_file(self, mount_id, file_id, offset, data): 417*9c5db199SXin Li """ 418*9c5db199SXin Li Writes data to a file. 419*9c5db199SXin Li 420*9c5db199SXin Li @param mount_id: Mount ID from the mounted share. 421*9c5db199SXin Li @param file_id: ID of the file to be written to. 422*9c5db199SXin Li @param offset: Offset of the file to start writing to. 423*9c5db199SXin Li @param data: Data to be written. 424*9c5db199SXin Li 425*9c5db199SXin Li @return ErrorType returned from the D-Bus call. 426*9c5db199SXin Li 427*9c5db199SXin Li """ 428*9c5db199SXin Li 429*9c5db199SXin Li logging.info("Writing to file: %s", file_id) 430*9c5db199SXin Li 431*9c5db199SXin Li from directory_entry_pb2 import WriteFileOptionsProto 432*9c5db199SXin Li 433*9c5db199SXin Li proto = WriteFileOptionsProto() 434*9c5db199SXin Li proto.mount_id = mount_id 435*9c5db199SXin Li proto.file_id = file_id 436*9c5db199SXin Li proto.offset = offset 437*9c5db199SXin Li proto.length = len(data) 438*9c5db199SXin Li 439*9c5db199SXin Li with self.DataFd(data) as data_fd: 440*9c5db199SXin Li return self._smbproviderd.WriteFile(_proto_to_blob(proto), 441*9c5db199SXin Li dbus.types.UnixFd(data_fd), 442*9c5db199SXin Li timeout=self._DEFAULT_TIMEOUT, 443*9c5db199SXin Li byte_arrays=True) 444*9c5db199SXin Li 445*9c5db199SXin Li class DataFd(object): 446*9c5db199SXin Li """ 447*9c5db199SXin Li Writes data into a file descriptor. 448*9c5db199SXin Li 449*9c5db199SXin Li Use in a 'with' statement to automatically close the returned file 450*9c5db199SXin Li descriptor. 451*9c5db199SXin Li 452*9c5db199SXin Li @param data: Data string. 453*9c5db199SXin Li 454*9c5db199SXin Li @return A file descriptor (pipe) containing the data. 455*9c5db199SXin Li 456*9c5db199SXin Li """ 457*9c5db199SXin Li 458*9c5db199SXin Li def __init__(self, data): 459*9c5db199SXin Li self._data = data 460*9c5db199SXin Li self._read_fd = None 461*9c5db199SXin Li 462*9c5db199SXin Li def __enter__(self): 463*9c5db199SXin Li """Creates the data file descriptor.""" 464*9c5db199SXin Li 465*9c5db199SXin Li self._read_fd, write_fd = os.pipe() 466*9c5db199SXin Li os.write(write_fd, self._data) 467*9c5db199SXin Li os.close(write_fd) 468*9c5db199SXin Li return self._read_fd 469*9c5db199SXin Li 470*9c5db199SXin Li def __exit__(self, mytype, value, traceback): 471*9c5db199SXin Li """Closes the data file descriptor again.""" 472*9c5db199SXin Li 473*9c5db199SXin Li if self._read_fd: 474*9c5db199SXin Li os.close(self._read_fd) 475