diff options
Diffstat (limited to 'mountconfig')
-rwxr-xr-x | mountconfig/MicroHAL.py | 884 | ||||
-rw-r--r-- | mountconfig/SMBShareSelectDialog.py | 573 | ||||
-rw-r--r-- | mountconfig/SimpleCommandRunner.py | 69 | ||||
-rw-r--r-- | mountconfig/fuser.py | 299 | ||||
-rw-r--r-- | mountconfig/fuser_ui.ui | 352 | ||||
-rw-r--r-- | mountconfig/mountconfig.desktop | 51 | ||||
-rwxr-xr-x | mountconfig/mountconfig.py | 3303 | ||||
-rw-r--r-- | mountconfig/sizeview.py | 504 |
8 files changed, 6035 insertions, 0 deletions
diff --git a/mountconfig/MicroHAL.py b/mountconfig/MicroHAL.py new file mode 100755 index 0000000..9ce8b83 --- /dev/null +++ b/mountconfig/MicroHAL.py @@ -0,0 +1,884 @@ +#!/usr/bin/python +########################################################################### +# MicroHAL.py - # +# ------------------------------ # +# begin : Tue Oct 30 2004 # +# copyright : (C) 2004 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### + +import os +import os.path +from SimpleCommandRunner import * + +############################################################################ +class MicroHAL__(object): + + # Major device numbers for Linux block devices that support partitions. + partitionblockdevs = [ + 3, # IDE harddisks + 8, # SCSI disks + 13, # 8-bit MFM/RLL/IDE controller + 14, # BIOS harddrive callback support {2.6} + 21, # Acorn MFM hard drive interface + 22, # Second IDE hard disk/CD-ROM interface + 28, # ACSI disk (68k/Atari) + 33, # Third IDE hard disk/CD-ROM interface + 34, # Fourth IDE hard disk/CD-ROM interface + 36, # MCA ESDI hard disk + 44, # Flash Translation Layer (FTL) filesystems + 45, # Parallel port IDE disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 49, # Mylex DAC960 PCI RAID controller; second controller + 50, # Mylex DAC960 PCI RAID controller; third controller + 51, # Mylex DAC960 PCI RAID controller; fourth controller + 52, # Mylex DAC960 PCI RAID controller; fifth controller + 53, # Mylex DAC960 PCI RAID controller; sixth controller + 54, # Mylex DAC960 PCI RAID controller; seventh controller + 55, # Mylex DAC960 PCI RAID controller; eigth controller + 56, # Fifth IDE hard disk/CD-ROM interface + 57, # Sixth IDE hard disk/CD-ROM interface + 65, # SCSI disk devices (16-31) + 66, # SCSI disk devices (32-47) + 67, # SCSI disk devices (48-63) + 68, # SCSI disk devices (64-79) + 69, # SCSI disk devices (80-95) + 70, # SCSI disk devices (96-111) + 71, # SCSI disk devices (112-127) + 72, # Compaq Intelligent Drive Array, first controller + 73, # Compaq Intelligent Drive Array, second controller + 74, # Compaq Intelligent Drive Array, third controller + 75, # Compaq Intelligent Drive Array, fourth controller + 76, # Compaq Intelligent Drive Array, fifth controller + 77, # Compaq Intelligent Drive Array, sixth controller + 78, # Compaq Intelligent Drive Array, seventh controller + 79, # Compaq Intelligent Drive Array, eigth controller + 80, # I2O hard disk + 81, # I2O hard disk + 82, # I2O hard disk + 83, # I2O hard disk + 84, # I2O hard disk + 85, # I2O hard disk + 86, # I2O hard disk + 87, # I2O hard disk + 88, # Seventh IDE hard disk/CD-ROM interface + 89, # Eighth IDE hard disk/CD-ROM interface + 90, # Ninth IDE hard disk/CD-ROM interface + 91, # Tenth IDE hard disk/CD-ROM interface + 92, # PPDD encrypted disk driver + 95, # IBM S/390 DASD block storage + 101, # AMI HyperDisk RAID controller + 102, # Compressed block device + 104, # Compaq Next Generation Drive Array, first controller + 105, # Compaq Next Generation Drive Array, second controller + 106, # Compaq Next Generation Drive Array, third controller + 107, # Compaq Next Generation Drive Array, fourth controller + 108, # Compaq Next Generation Drive Array, fifth controller + 109, # Compaq Next Generation Drive Array, sixth controller + 110, # Compaq Next Generation Drive Array, seventh controller + 111, # Compaq Next Generation Drive Array, eigth controller + 112, # IBM iSeries virtual disk + 114, # IDE BIOS powered software RAID interfaces such as the Promise Fastrak + 128, # SCSI disk devices (128-143) + 129, # SCSI disk devices (144-159) + 130, # SCSI disk devices (160-175) + 131, # SCSI disk devices (176-191) + 132, # SCSI disk devices (192-207) + 133, # SCSI disk devices (208-223) + 134, # SCSI disk devices (224-239) + 135, # SCSI disk devices (240-255) + 136, # Mylex DAC960 PCI RAID controller; ninth controller + 137, # Mylex DAC960 PCI RAID controller; tenth controller + 138, # Mylex DAC960 PCI RAID controller; eleventh controller + 139, # Mylex DAC960 PCI RAID controller; twelfth controller + 140, # Mylex DAC960 PCI RAID controller; thirteenth controller + 141, # Mylex DAC960 PCI RAID controller; fourteenth controller + 142, # Mylex DAC960 PCI RAID controller; fifteenth controller + 143, # Mylex DAC960 PCI RAID controller; sixteenth controller + 160, # Promise SX8 8-port SATA Disks on First Controller + 161 # Promise SX8 8-port SATA Disks on Second Controller + ] + + floppydevs = [ + 2, # Floppy disks + 40 # Syquest EZ135 parallel port removable drive + ] + + cdromsdevs = [ + 11, # SCSI CD-ROM devices + 12, # MSCDEX CD-ROM callback support {2.6} + 15, # Sony CDU-31A/CDU-33A CD-ROM + 16, # GoldStar CD-ROM + 17, # Optics Storage CD-ROM + 18, # Sanyo CD-ROM + 20, # Hitachi CD-ROM (under development) + 23, # Mitsumi proprietary CD-ROM + 24, # Sony CDU-535 CD-ROM + 25, # First Matsushita (Panasonic/SoundBlaster) CD-ROM + 26, # Second Matsushita (Panasonic/SoundBlaster) CD-ROM + 27, # Third Matsushita (Panasonic/SoundBlaster) CD-ROM + 28, # Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM + 29, # Aztech/Orchid/Okano/Wearnes CD-ROM + 30, # Philips LMS CM-205 CD-ROM + 32, # Philips LMS CM-206 CD-ROM + 41, # MicroSolutions BackPack parallel port CD-ROM + 46, # Parallel port ATAPI CD-ROM devices + 47, # Parallel port ATAPI disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 113 # IBM iSeries virtual CD-ROM + ] + + burnerpacketdevs = [ + 97 # Packet writing for CD/DVD devices + ] + + # We provide a mapping between filesystems and kernelmodules, so filesystems + # that are built as modules can be loaded on demand. (In fact, mountconfig will + # load all filesystem modules needed to be able to mount all fstab entries.) + FilesystemProcDriver = [ + # fstab name, /proc name, kernel module name + ('auto','autofs','autofs4'), + ('iso9660','iso9660','isofs'), + ('nfs','nfsd','nfs') + ] + + ############################################################################ + def __init__(self): + self.devices = None + self.supportedfs = None + self.partitionsizelines = None + + ############################################################################ + def getDevices(self): + if self.devices is None: + self.devices = [] + # Scan through /sys/block for devices. Find out which disks are + # installed and should be shown in the listview. For real + # disks we put a 'group' in the listview and under the group + # we list the partitions, whether they are mounted or not. + # FIXME: Check if sysfs is mounted. + blockdevices = os.listdir("/sys/block") + blockdevices.sort() + + for blockdevice in blockdevices: + # We are looking for block devices that represent hard disks or + # things that have partitions. + # Grab the major device number + fhandle = open(os.path.join("/sys/block",blockdevice,"dev")) + devnumbers = fhandle.read() + fhandle.close() + devnum = int(devnumbers.split(":")[0]) + # Is it on our list of partition devices? + if devnum in MicroHAL.partitionblockdevs: + fulldevice = os.path.join("/dev",blockdevice) + + # Check for removable devices. + fhandle = open(os.path.join("/sys/block",blockdevice,"removable")) + removable = fhandle.read().strip()=="1" + fhandle.close() + + if not removable: + newdisk = Disk() + else: + if os.readlink(os.path.join("/sys/block",blockdevice,"device")).split(os.path.sep)[5].startswith("usb"): + newdisk = USBDisk() + else: + newdisk = RemovableDisk() + newdisk.dev = fulldevice + newdisk.major = devnum + newdisk.removable = removable + newdisk.modelname = self.getModelName(fulldevice) + + if not removable or isinstance(newdisk, USBDisk): + # We have a not removable block device or a USB Disk here. + partitions = os.listdir(os.path.join("/sys/block",blockdevice)) + partitions.sort() + i = 1 + for partition in partitions: + # Look for a partitions device names and not the other + # stuff that lives in the directory. + if partition.startswith(blockdevice): + fullpartition = os.path.join("/dev",partition) + newpartition = Partition() + newpartition.dev = fullpartition + newpartition.size = self.getPartitionSize(fullpartition) + newpartition.num = i + newdisk.partitions.append(newpartition) + i += 1 + self.devices.append(newdisk) + + elif devnum in MicroHAL.cdromsdevs: + fulldevice = os.path.join("/dev",blockdevice) + newdisk = RemovableDisk() + newdisk.dev = fulldevice + newdisk.major = devnum + newdisk.modelname = self.getModelName(fulldevice) + self.devices.append(newdisk) + + elif devnum in MicroHAL.burnerpacketdevs: + fulldevice = os.path.join("/dev",blockdevice) + newdisk = BurnerDisk(self) + newdisk.dev = fulldevice + newdisk.major = devnum + newdisk.modelname = self.getModelName(fulldevice) + + self.devices.append(newdisk) + + return self.devices[:] + + ############################################################################ + def getPartitionSize(self,devicename): + partitionname = os.path.basename(devicename) + + if self.partitionsizelines is None: + fhandle = open('/proc/partitions') + self.partitionsizelines = fhandle.readlines() + fhandle.close() + + i = 0 + for line in self.partitionsizelines: + if i>=2: + (major, minor, blocks, name) = line.split() + if name==partitionname: + blocks = int(blocks) # 1K blocks now. + if blocks<1024: + return str(blocks)+" Kb" + if blocks<1024*1024: + return str(round(float(blocks)/1024.0,1))+" Mb" + blocks /= 1024 + if blocks<1024*1024: + return str(round(float(blocks)/1024.0,1))+" Gb" + blocks /= 1024 + return str(round(float(blocks)/1024.0,1))+" Tb" + i += 1 + return None + + ############################################################################ + def getIDEModel(self,devname): + try: + fhandle = open(os.path.join("/proc/ide",os.path.basename(devname),"model")) + model = fhandle.read() + fhandle.close() + return model.strip() + except (OSError, IOError): + return None + + ############################################################################ + def getSCSIModel(self,devname): + try: + fhandle_model = open(os.path.join("/sys/block",os.path.basename(devname),"device/model")) + fhandle_vendor = open(os.path.join("/sys/block",os.path.basename(devname),"device/vendor")) + model = fhandle_model.read()[:-1] + vendor = fhandle_vendor.read()[:-1] + fhandle_model.close() + fhandle_vendor.close() + except (OSError, IOError): + pass + if len(model) + len(vendor) == 0: + return None + return vendor + " " + model + + ############################################################################ + def getModelName(self,devname): + modelname = self.getIDEModel(devname) + if modelname is None: + modelname = self.getSCSIModel(devname) + if modelname is None: + modelname = devname + return " '"+modelname+"'" + + ############################################################################ + def getSupportedFileSystems(self): + if self.supportedfs is None: + if os.path.isfile("/proc/filesystems"): + fhandle = open("/proc/filesystems") + self.supportedfs = [] + for fs in fhandle.readlines(): + try: + self.supportedfs.append(fs.strip().split()[1]) + except IndexError: + self.supportedfs.append(fs.strip().split()[0]) + # The following filesystems aren't found there, but usually they are + # supported. + self.supportedfs.extend(('swap','shm')) + return self.supportedfs[:] + + ############################################################################ + def isSupportedFileSystem(self,fs): + # Look up the /proc and kernel driver name for the given filesystem type. + module = fs + proc = fs + for entry in self.FilesystemProcDriver: + if entry[0]==fs: + proc = entry[1] + module = entry[2] + + if proc not in self.getSupportedFileSystems(): + # The filesystem is not supported by the running kernel, + # but it might be built as module, so we try to load that. + retval, msg = SimpleCommandRunner().run(["/sbin/modprobe",module]) + if retval > 0: + print msg + print "Couldn't load driver " + module + " for filesystem " + fs + # Force refresh of list of supported filesystems + self.supportedfs = None + return proc in self.getSupportedFileSystems() + +############################################################################ +class Device(object): + def __init__(self): + self.dev = None + self.major = None + self.removable = None + self.uuid = None + self.label = None + + def getDev(self): + return self.dev + + def getMajor(self): + return self.major + + def getName(self): + return self.dev + + def getUUID(self): + if not self.uuid: + return "" + return self.uuid + + def getLabel(self): + if not self.label: + return "" + return self.label + + def isRemovable(self): + return self.removable + + def __str__(self): + return "Name: %s, Device: %s, Major: %i, " % (self.getName(), + self.getDev(), + self.getMajor()) + +############################################################################ +class Disk(Device): + def __init__(self): + super(Disk,self).__init__() + self.removable = False + self.partitions = [] + self.modelname = None + self.iconname = "hi16-hdd" + + def getModelName(self): + return self.modelname + + def getName(self): + if self.getModelName(): + return i18n("Disk ")+self.getModelName() + else: + return i18n("Unknown Disk") + + def getPartitions(self): + return self.partitions[:] + + def appendPartition(self,partition): + self.partitions.append(partition) + def cmpNum(a,b): return cmp(a.num,b.num) + self.partitions.sort(cmpNum) + + def __str__(self): + x = Device.__str__(self) + "Partitions: [" + for part in self.partitions: + x += "[" + x += str(part) + x += "], " + x += "]," + return x + +############################################################################ +class RemovableDisk(Disk): + def __init__(self): + super(RemovableDisk,self).__init__() + self.iconname = "hi16-cdrom" + self.removable = True + + def getName(self): + return "Optical Disk "+self.getModelName() + +############################################################################ +class USBDisk(Disk): + def __init__(self): + super(USBDisk,self).__init__() + self.iconname = "hi16-usbpen" + self.removable = True + + def getName(self): + return "Removable USB Disk "+self.getModelName() + +############################################################################ +class BurnerDisk(RemovableDisk): + def __init__(self): + super(BurnerDisk,self).__init__() + self.iconname = "hi16-burner" + + def getName(self): + return "Burner "+self.modelname + +############################################################################ +class Floppy(Device): + def isRemovable(self): + return True + + def getName(self): + return "Floppy" + +############################################################################ +class Partition(Device): + def __init__(self): + super(Partition,self).__init__() + self.num = None + self.size = None + self.iconname = "hi16-hdd" + + def getName(self): + return str(self.num)+" Partition "+ self.getSize() + # A group item for all of the other kernel/system mount entries. + + def getSize(self): + return self.size + + def __str__(self): + return "Device: %s, Num: %i, Size: %s, Label: %s, UUID: %s" % (self.dev, self.num, self.getSize(), + self.getLabel(), self.getUUID()) + +############################################################################ +class FakeSystemDevice(object): + def getName(self): return "System" + def getIconName(self): return "hi16-blockdevice" + +############################################################################ +class MicroHAL(object): + + # Major device numbers for Linux block devices that support partitions. + partitionblockdevs = [ + 3, # IDE harddisks + 8, # SCSI disks + 13, # 8-bit MFM/RLL/IDE controller + 14, # BIOS harddrive callback support {2.6} + 21, # Acorn MFM hard drive interface + 22, # Second IDE hard disk/CD-ROM interface + 28, # ACSI disk (68k/Atari) + 33, # Third IDE hard disk/CD-ROM interface + 34, # Fourth IDE hard disk/CD-ROM interface + 36, # MCA ESDI hard disk + 44, # Flash Translation Layer (FTL) filesystems + 45, # Parallel port IDE disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 49, # Mylex DAC960 PCI RAID controller; second controller + 50, # Mylex DAC960 PCI RAID controller; third controller + 51, # Mylex DAC960 PCI RAID controller; fourth controller + 52, # Mylex DAC960 PCI RAID controller; fifth controller + 53, # Mylex DAC960 PCI RAID controller; sixth controller + 54, # Mylex DAC960 PCI RAID controller; seventh controller + 55, # Mylex DAC960 PCI RAID controller; eigth controller + 56, # Fifth IDE hard disk/CD-ROM interface + 57, # Sixth IDE hard disk/CD-ROM interface + 65, # SCSI disk devices (16-31) + 66, # SCSI disk devices (32-47) + 67, # SCSI disk devices (48-63) + 68, # SCSI disk devices (64-79) + 69, # SCSI disk devices (80-95) + 70, # SCSI disk devices (96-111) + 71, # SCSI disk devices (112-127) + 72, # Compaq Intelligent Drive Array, first controller + 73, # Compaq Intelligent Drive Array, second controller + 74, # Compaq Intelligent Drive Array, third controller + 75, # Compaq Intelligent Drive Array, fourth controller + 76, # Compaq Intelligent Drive Array, fifth controller + 77, # Compaq Intelligent Drive Array, sixth controller + 78, # Compaq Intelligent Drive Array, seventh controller + 79, # Compaq Intelligent Drive Array, eigth controller + 80, # I2O hard disk + 81, # I2O hard disk + 82, # I2O hard disk + 83, # I2O hard disk + 84, # I2O hard disk + 85, # I2O hard disk + 86, # I2O hard disk + 87, # I2O hard disk + 88, # Seventh IDE hard disk/CD-ROM interface + 89, # Eighth IDE hard disk/CD-ROM interface + 90, # Ninth IDE hard disk/CD-ROM interface + 91, # Tenth IDE hard disk/CD-ROM interface + 92, # PPDD encrypted disk driver + 95, # IBM S/390 DASD block storage + 101, # AMI HyperDisk RAID controller + 102, # Compressed block device + 104, # Compaq Next Generation Drive Array, first controller + 105, # Compaq Next Generation Drive Array, second controller + 106, # Compaq Next Generation Drive Array, third controller + 107, # Compaq Next Generation Drive Array, fourth controller + 108, # Compaq Next Generation Drive Array, fifth controller + 109, # Compaq Next Generation Drive Array, sixth controller + 110, # Compaq Next Generation Drive Array, seventh controller + 111, # Compaq Next Generation Drive Array, eigth controller + 112, # IBM iSeries virtual disk + 114, # IDE BIOS powered software RAID interfaces such as the Promise Fastrak + 128, # SCSI disk devices (128-143) + 129, # SCSI disk devices (144-159) + 130, # SCSI disk devices (160-175) + 131, # SCSI disk devices (176-191) + 132, # SCSI disk devices (192-207) + 133, # SCSI disk devices (208-223) + 134, # SCSI disk devices (224-239) + 135, # SCSI disk devices (240-255) + 136, # Mylex DAC960 PCI RAID controller; ninth controller + 137, # Mylex DAC960 PCI RAID controller; tenth controller + 138, # Mylex DAC960 PCI RAID controller; eleventh controller + 139, # Mylex DAC960 PCI RAID controller; twelfth controller + 140, # Mylex DAC960 PCI RAID controller; thirteenth controller + 141, # Mylex DAC960 PCI RAID controller; fourteenth controller + 142, # Mylex DAC960 PCI RAID controller; fifteenth controller + 143, # Mylex DAC960 PCI RAID controller; sixteenth controller + 160, # Promise SX8 8-port SATA Disks on First Controller + 161 # Promise SX8 8-port SATA Disks on Second Controller + ] + + floppydevs = [ + 2, # Floppy disks + 40 # Syquest EZ135 parallel port removable drive + ] + + cdromsdevs = [ + 11, # SCSI CD-ROM devices + 12, # MSCDEX CD-ROM callback support {2.6} + 15, # Sony CDU-31A/CDU-33A CD-ROM + 16, # GoldStar CD-ROM + 17, # Optics Storage CD-ROM + 18, # Sanyo CD-ROM + 20, # Hitachi CD-ROM (under development) + 23, # Mitsumi proprietary CD-ROM + 24, # Sony CDU-535 CD-ROM + 25, # First Matsushita (Panasonic/SoundBlaster) CD-ROM + 26, # Second Matsushita (Panasonic/SoundBlaster) CD-ROM + 27, # Third Matsushita (Panasonic/SoundBlaster) CD-ROM + 28, # Fourth Matsushita (Panasonic/SoundBlaster) CD-ROM + 29, # Aztech/Orchid/Okano/Wearnes CD-ROM + 30, # Philips LMS CM-205 CD-ROM + 32, # Philips LMS CM-206 CD-ROM + 41, # MicroSolutions BackPack parallel port CD-ROM + 46, # Parallel port ATAPI CD-ROM devices + 47, # Parallel port ATAPI disk devices + 48, # Mylex DAC960 PCI RAID controller; first controller + 113 # IBM iSeries virtual CD-ROM + ] + + burnerpacketdevs = [ + 97 # Packet writing for CD/DVD devices + ] + + # We provide a mapping between filesystems and kernelmodules, so filesystems + # that are built as modules can be loaded on demand. (In fact, mountconfig will + # load all filesystem modules needed to be able to mount all fstab entries.) + FilesystemProcDriver = [ + # fstab name, /proc name, kernel module name + ('auto','autofs','autofs4'), + ('iso9660','iso9660','isofs'), + ('nfs','nfsd','nfs') + ] + + ############################################################################ + def __init__(self): + self.devices = None + self.supportedfs = None + + ############################################################################ + def getDevices(self): + if self.devices is None: + self.devices = [] + + retval, msg = SimpleCommandRunner().run(["/usr/bin/lshal"]) + if retval > 0: + return [] + + partition_to_uid = {} + uid_to_disk = {} + + READING_TOP = 0 + READING_DEVICE = 1 + state = READING_TOP + + parsed_hash = None + current_uid = None + + for line in msg.split('\n'): + + if state==READING_TOP: + if line.startswith("udi ="): + parsed_hash = {} + current_uid = self._parseString(line[6:]) + state = READING_DEVICE + + elif state==READING_DEVICE: + if line=="" or not line.startswith(" "): + # Detect the end of this block of device data. + state = READING_TOP + + if u"info.category" in parsed_hash: + + new_device = None + + capabilities_string = u" ".join(parsed_hash[u"info.capabilities"]) + capabilities = self._parseStringList(capabilities_string) + + category = self._parseString(' '.join(parsed_hash[u"info.category"])) + if category==u"volume": + # Is it a volume? + + is_disc = parsed_hash.get(u"volume.is_disc") + if is_disc is not None and is_disc[0]=='true': + continue + + is_partition = parsed_hash.get(u"volume.is_partition") + if is_partition is not None: + is_partition = is_partition[0] + + if is_partition=='true': + new_device = Partition() + new_device.num = int(parsed_hash[u"volume.partition.number"][0]) + partition_to_uid[new_device] = current_uid + + if u"info.parent" in parsed_hash: + parent_uid = self._parseString(' '.join(parsed_hash[u"info.parent"])) + partition_to_uid[new_device] = parent_uid + + else: + new_device = Disk() + uid_to_disk[current_uid] = new_device + + if u"volume.uuid" in parsed_hash: + new_device.uuid = self._parseString(' '.join(parsed_hash[u"volume.uuid"])) + + if u"volume.label" in parsed_hash: + new_device.label = self._parseString(parsed_hash[u"volume.label"][0]) + + if u"volume.size" in parsed_hash: + size = parsed_hash[u"volume.size"][0] + new_device.size = self.formatSizeBytes(int(size)) + else: + new_device.size = "?" + + + # is it a storage device? + elif category==u"storage": + storage_model = self._parseString(' '.join(parsed_hash[u"storage.model"])) + storage_removable = parsed_hash[u"storage.removable"][0]==u"true" + + if u"storage.cdrom" in capabilities: + + if u"storage.cdrom.cdrw" in parsed_hash \ + and parsed_hash[u"storage.cdrom.cdrw"][0]==u"true": + new_device = BurnerDisk() + else: + new_device= RemovableDisk() + + elif u"storage.floppy" in capabilities: + new_device = FloppyDevice() + else: + if u"storage.bus" in parsed_hash \ + and self._parseString(' '.join(parsed_hash[u"storage.bus"]))==u"usb": + + new_device = USBDisk() + else: + new_device = Disk() + + new_device.modelname = storage_model + uid_to_disk[current_uid] = new_device + else: + # Not interesting, skip it. + continue + + # Handle the generic properties. + new_device.dev = self._parseString(' '.join(parsed_hash[u"block.device"])) + new_device.major = int(parsed_hash[u"block.major"][0]) + + self.devices.append(new_device) + + else: + # Keep on accumulating info about this device. + parts = line.split() + parsed_hash[ parts[0] ] = parts[2:] + + # Attach the partitions to thier devices. + for partition in partition_to_uid.keys(): + parent = partition_to_uid[partition] + if parent in uid_to_disk.keys(): + parent_device = uid_to_disk[parent] + parent_device.appendPartition(partition) + self.devices.remove(partition) + + return self.devices[:] + + ############################################################################ + def _parseStringList(self,source_string): + STATE_TOP = 0 + STATE_STRING = 1 + + state = STATE_TOP + current_string = "" + string_list = [] + for c in source_string: + if state==STATE_TOP: + if c=='}': + break + if c=="'": + state = STATE_STRING + else: + if c=="'": + state = STATE_TOP + string_list.append(current_string) + current_string = "" + else: + current_string += c + + return string_list + + ############################################################################ + def _parseString(self,source_string): + STATE_TOP = 0 + STATE_STRING = 1 + + state = STATE_TOP + current_string = "" + for c in source_string: + if state==STATE_TOP: + if c=="'": + state = STATE_STRING + else: + if c=="'": + break + else: + current_string += c + return current_string + + ############################################################################ + def formatSizeBytes(self,size): + if size<1024: + return str(size+" B") + if size<1024*1042: + return str(round(float(size)/1024.0,1))+" Kb" + size /= 1024 + if size<1024*1024: + return str(round(float(size)/1024.0,1))+" Mb" + size /= 1024 + if size<1024*1024: + return str(round(float(size)/1024.0,1))+" Gb" + size /= 1024 + return str(round(float(size)/1024.0,1))+" Tb" + + ############################################################################ + def getSupportedFileSystems(self): + if self.supportedfs is None: + if os.path.isfile("/proc/filesystems"): + fhandle = open("/proc/filesystems") + self.supportedfs = [] + for fs in fhandle.readlines(): + try: + self.supportedfs.append(fs.strip().split()[1]) + except IndexError: + self.supportedfs.append(fs.strip().split()[0]) + # The following filesystems aren't found there, but usually they are + # supported. + self.supportedfs.extend(('swap','shm')) + return self.supportedfs[:] + + ############################################################################ + def isSupportedFileSystem(self,fs): + # Look up the /proc and kernel driver name for the given filesystem type. + module = fs + proc = fs + for entry in self.FilesystemProcDriver: + if entry[0]==fs: + proc = entry[1] + module = entry[2] + + if proc not in self.getSupportedFileSystems(): + # The filesystem is not supported by the running kernel, + # but it might be built as module, so we try to load that. + retval, msg = SimpleCommandRunner().run(["/sbin/modprobe",module]) + if retval > 0: + print msg + print "Couldn't load driver " + module + " for filesystem " + fs + # Force refresh of list of supported filesystems + self.supportedfs = None + return proc in self.getSupportedFileSystems() + + ############################################################################ + def getDeviceByLabel(self, label): + for device in self.getDevices(): + if device.getLabel()==label: + return device + + if isinstance(device,Disk): + for partition in device.getPartitions(): + if partition.getLabel()==label: + return partition + return None + + def getLabelByDevice(self, device): + for item in self.getDevices(): + for partition in item.partitions: + if partition.dev==device: + return partition.label + print "No Label found for ",device + return "" + + def getUUIDByDevice(self, device): + for item in self.getDevices(): + for partition in item.partitions: + #print partition, partition.getUUID() + if partition.dev==device: + return partition.uuid + print "No UUID found for ",device + return "" + + def getDeviceByUUID(self, uuid): + for device in self.getDevices(): + if device.getUUID()==uuid: + return device + + if isinstance(device,Disk): + for partition in device.getPartitions(): + if partition.getUUID()==uuid: + return partition + + return None + +############################################################################ +if __name__=='__main__': + hal = MicroHAL() + for item in hal.getDevices(): + print(str(item)) + + print + + #""" + for item in hal.getDevices(): + for partition in item.partitions: + print partition, partition.getLabel() + #""" + #realhal = RealHAL() + #for item in realhal.getDevices(): + # print(str(item)) + + print + + diff --git a/mountconfig/SMBShareSelectDialog.py b/mountconfig/SMBShareSelectDialog.py new file mode 100644 index 0000000..171cf5d --- /dev/null +++ b/mountconfig/SMBShareSelectDialog.py @@ -0,0 +1,573 @@ +########################################################################### +# SMBShareSelectDialog.py - Dialog for selecting an SMB share on a network# +# ------------------------------ # +# begin : Tue Oct 30 2004 # +# copyright : (C) 2004 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### + +from qt import * +from kdeui import * +from kdecore import * +from kio import * + +############################################################################ +class SMBShareSelectDialog(KDialogBase): + + STATUS_IDLE = 0 + STATUS_SEARCH_TOP_LEVEL = 1 + STATUS_SEARCH = 2 + STATUS_RESOLVE = 3 + + ######################################################################## + def __init__(self,parent,name=None): + super(SMBShareSelectDialog,self).__init__(parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel) + self.updatinggui = False + + self.resize(600,400) + + vbox = self.makeVBoxMainWidget() + + hbox = QHBox(vbox) + hbox.setSpacing(self.spacingHint()) + tmplabel = QLabel(hbox) + tmplabel.setPixmap(UserIcon("hi32-samba")) + + hbox.setStretchFactor(tmplabel,0) + + self.headinglabel = QLabel(hbox) + self.headinglabel.setText(i18n("Select a network share")) + hbox.setStretchFactor(self.headinglabel,1) + + hbox2 = QHBox(vbox) + + # The main treeview where the action happens. + self.treeview = KListView(hbox2) + self.treeview.addColumn("(hidden)") + self.treeview.header().hide() + self.treeview.setRootIsDecorated(True) + + self.connect(self.treeview,SIGNAL("expanded(QListViewItem *)"),self.slotNodeExpanded) + self.connect(self.treeview,SIGNAL("selectionChanged(QListViewItem *)"),self.slotNodeSelected) + self.connect(self.treeview,SIGNAL("clicked(QListViewItem *)"),self.slotClicked) + self.dirlister = KDirLister() + self.dirlister.setDirOnlyMode(True) + self.dirlister.setAutoUpdate(False) + self.dirlister.setAutoErrorHandlingEnabled(True,self) + self.connect(self.dirlister,SIGNAL("newItems(const KFileItemList &)"),self.slotNewItems) + self.connect(self.dirlister,SIGNAL("completed()"),self.slotDirListCompleted) + self.connect(self.dirlister,SIGNAL("canceled()"),self.slotDirListCanceled) + self.connect(self.dirlister,SIGNAL("redirection(const KURL &,const KURL &)"),self.slotDirListRedirection) + self.enableButtonOK(False) + + # The "Connect as" part + widget = QWidget(hbox2) + grid = QGridLayout(widget,6,4,KDialog.spacingHint()) + grid.setRowStretch(5,1) + + tmplabel = QLabel(widget) + tmplabel.setPixmap(UserIcon("hi16-password")) + grid.addWidget(tmplabel,0,0) + + self.connectaslabel = QLabel(widget) + self.connectaslabel.setText("Connect to 'XXX' as:") + grid.addMultiCellWidget(self.connectaslabel,0,0,1,3) + + self.guestradio = QRadioButton(widget) + self.guestradio.setChecked(True) + grid.addWidget(self.guestradio,1,1) + tmplabel = QLabel(widget) + tmplabel.setText(i18n("Guest")) + grid.addWidget(tmplabel,1,2) + self.connect(self.guestradio,SIGNAL("stateChanged(int)"),self.slotGuestRadioClicked) + + self.userradio = QRadioButton(widget) + grid.addWidget(self.userradio,2,1) + tmplabel = QLabel(widget) + tmplabel.setText(i18n("Username:")) + grid.addWidget(tmplabel,2,2) + self.connect(self.userradio,SIGNAL("stateChanged(int)"),self.slotUserRadioClicked) + + self.usernameedit = KLineEdit(widget) + grid.addWidget(self.usernameedit,2,3) + self.connect(self.usernameedit,SIGNAL("textChanged(const QString &)"),self.slotUsernameChanged) + + tmplabel = QLabel(widget) + tmplabel.setText(i18n("Password:")) + grid.addWidget(tmplabel,3,2) + + self.passwordedit = KLineEdit(widget) + grid.addWidget(self.passwordedit,3,3) + + self.reconnectbutton = KPushButton(i18n("Reconnect now"),widget) + grid.addMultiCellWidget(self.reconnectbutton,4,4,1,3) + self.connect(self.reconnectbutton,SIGNAL("clicked()"),self.slotReconnectClicked) + + self.dirlistertimer = None + + ######################################################################## + def choose(self,currenturl): + self.lookupqueue = [] + self.selecteditem = None + + self.treeview.clear() + self.url_to_list_item_map = {} + + # Fill the first level + root_url = KURL("smb:/") + self.rootitem = SMBShareListViewItem(self.treeview, i18n("Network Neighbourhood"), root_url, self) + + self.searchurl = currenturl + self._updateConnectGUI() + self.enableButtonOK(False) + self._openDefaultURL() + + self.spintimerid = self.startTimer(250) + self.exec_loop() + self.stopResolve() + + self.killTimer(self.spintimerid) + + if self.result()==self.Accepted: + currenturl = self.selecteditem.getURL() + + self.url_to_list_item_map = None + + return currenturl + + ######################################################################## + def _openDefaultURL(self): + if self.searchurl is not None: + rc = self.rootitem.selectURL(self.searchurl) + if rc==self.rootitem.OPEN_SUCCESS: + self.currenturl = self.searchurl + self.searchurl = None + self.enableButtonOK(True) + elif rc==self.rootitem.OPEN_FAIL or rc==self.rootitem.OPEN_SUCCESS_INVALID: + self.searchurl = None + + ######################################################################## + def stopResolve(self): + if self.dirlistertimer is not None: + self.killTimer(self.dirlistertimer) + self.dirlister.stop() + for item in self.lookupqueue: + item.cancelResolve() + self.lookupqueue = [] + + self.searchurl = None # Stop trying to open this URL too. + + ######################################################################## + def setOpen(self,item,open): + if item.isResolved(): + KListView.setOpen(self.treeview,item,open) + else: + item.startResolve(True) + + ######################################################################## + def appendToResolveQueue(self,item): + if item not in self.lookupqueue: + self.lookupqueue.append(item) + self._startDirLister() + return True + else: + return False + + ######################################################################## + def slotNodeExpanded(self,item): + self.setOpen(item,True) + + ######################################################################## + def slotClicked(self): + if self.treeview.selectedItem() is None: + self.selecteditem = None + self._updateConnectGUI() + self.enableButtonOK(False) + + ######################################################################## + def slotNodeSelected(self,item): + self.selecteditem = item + self._updateConnectGUI() + self.enableButtonOK(item.getLevel()==item.LEVEL_DIR) + + if not self.selecteditem.isResolved(): + self.selecteditem.startResolve(False) + + ######################################################################## + def slotNewItems(self,items): + for entry in items: + newitem = SMBShareListViewItem(self.lookupqueue[0], unicode(entry.name()), KURL(entry.url()), self) + self.url_to_list_item_map[unicode(entry.url().prettyURL())] = newitem + # Notice how I copied the KURL object and QString (to a python string) + + ######################################################################## + def slotDirListCompleted(self): + item = self.lookupqueue[0] + item.setBusyIcon(False) + del self.lookupqueue[0] + + item.resolveComplete() + self._startDirLister() + + self._openDefaultURL() + + ######################################################################## + def slotDirListCanceled(self): + self.stopResolve() + + ######################################################################## + def slotDirListRedirection(self,oldUrl,newUrl): + list_item = self.url_to_list_item_map[unicode(oldUrl.prettyURL())] + list_item.setURL(KURL(newUrl)) # The copy is important. + + # Reselect the selected node. (This will force a refresh). + if self.selecteditem is not None: + self.updatinggui = True + self.slotNodeSelected(self.selecteditem) + self.updatinggui = False + + ######################################################################## + def slotUsernameChanged(self,newtext): + self.reconnectbutton.setEnabled(self.usernameedit.text()!="") + + ######################################################################## + def slotReconnectClicked(self): + if self.updatinggui: + return + self.updatinggui = True + + if self.selecteditem is None: # Sanity check. + return + + # The user wants to change how we connect to this remote machine. + + machineitem = self.selecteditem.getMachineItem() + if machineitem is None: + return # Shouldn't happen. + + self.stopResolve() + + # Grab the URL object before we delete the listviewitem that holds it. + selectedurl = self.selecteditem.getURL() + + # Close up the machine item and remove the items under the machine item. + machineitem.unresolve() + + # Set the username/password for the machine item. + if self.guestradio.isChecked(): + machineitem.getURL().setUser(QString.null) + machineitem.getURL().setPass(QString.null) + selectedurl.setUser(QString.null) + selectedurl.setPass(QString.null) + else: + machineitem.getURL().setUser(self.usernameedit.text()) + machineitem.getURL().setPass(self.passwordedit.text()) + selectedurl.setUser(self.usernameedit.text()) + selectedurl.setPass(self.passwordedit.text()) + self.selecteditem = None + self._updateConnectGUI() + + self.searchurl = selectedurl + self._openDefaultURL() + self.updatinggui = False + + ######################################################################## + def _startDirLister(self): + if self.dirlistertimer is None: + # Check the URL lister queue the next the event loop runs. + # Don't get all "recursed up"! + self.dirlistertimer = self.startTimer(0) + + ######################################################################## + def timerEvent(self,event): + KDialogBase.timerEvent(self,event) + if self.spintimerid==event.timerId(): + # Spin the current folder icon + if len(self.lookupqueue)!=0: + self.lookupqueue[0].setBusyIcon(True) + elif event.timerId()==self.dirlistertimer: + self.killTimer(self.dirlistertimer) + self.dirlistertimer = None + if self.dirlister.isFinished(): + if len(self.lookupqueue)!=0: + self.dirlister.openURL(self.lookupqueue[0].getURL()) + + ######################################################################## + def slotGuestRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + + if self.selecteditem is None: + return + + if state==QButton.Off: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + + selectedurl = self.selecteditem.getURL() + self.reconnectbutton.setEnabled(unicode(selectedurl.user())!="") + + self.updatinggui = False + + ######################################################################## + def slotUserRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + if state==QButton.Off: + self.userradio.setChecked(True) + self.guestradio.setChecked(False) + + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + + username = unicode(self.usernameedit.text()) + password = unicode(self.passwordedit.text()) + selectedurl = self.selecteditem.getURL() + if username!="" and password!="" and \ + ((unicode(selectedurl.user())!=username) or (unicode(selectedurl.pass_())!=password)): + self.reconnectbutton.setEnabled(True) + else: + self.reconnectbutton.setEnabled(False) + + self.updatinggui = False + + ######################################################################## + def _updateConnectGUI(self): + if self.selecteditem is not None: + selectedurl = self.selecteditem.getURL() + self.guestradio.setEnabled(True) + self.userradio.setEnabled(True) + self.usernameedit.setEnabled(selectedurl.hasUser()) + self.passwordedit.setEnabled(selectedurl.hasUser()) + self.connectaslabel.setText(i18n("Connect to '%1' as:").arg(selectedurl.host())) + if selectedurl.hasUser(): + self.guestradio.setChecked(False) + self.userradio.setChecked(True) + self.usernameedit.setText(selectedurl.user()) + self.passwordedit.setText(selectedurl.pass_()) + else: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setText("") + self.usernameedit.setText("") + self.reconnectbutton.setEnabled(False) + else: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.guestradio.setEnabled(False) + self.userradio.setEnabled(False) + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + self.connectaslabel.setText(i18n("Connect to 'machine' as:")) + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setText("") + self.usernameedit.setText("") + self.reconnectbutton.setEnabled(False) + +############################################################################ +class SMBShareListViewItem(KListViewItem): + # Return codes for selectURL() + OPEN_SUCCESS = 1 + OPEN_SUCCESS_INVALID = 2 + OPEN_FAIL = 0 + OPEN_BUSY = 3 + + # Node types. + LEVEL_ROOT = 0 + LEVEL_WORKGROUP = 1 + LEVEL_MACHINE = 2 + LEVEL_DIR = 3 # and deeper. + + ######################################################################## + def __init__(self,parentitem,name,url,smbdialog): + KListViewItem.__init__(self,parentitem,name) + if not isinstance(parentitem,SMBShareListViewItem): + self._setIcon(0) + self.setSelectable(False) + else: + self._setIcon(parentitem.depth()+1) + self.setSelectable(parentitem.getLevel()>=self.LEVEL_WORKGROUP) + self.setExpandable(True) + + if url.hasPath() and url.path(-1)!="/": + parts = [x for x in unicode(url.path(-1)).split("/") if x!=""] + self.component = parts[-1].lower() + elif url.hasHost(): + self.component = unicode(url.host()).lower() + else: + self.component = None + + self.smbdialog = smbdialog + self.resolved = False + self.url = url + self.autoopen = False + self.animationcounter = 0 + + ######################################################################## + def getURL(self): + return self.url + + ######################################################################## + def setURL(self,url): + self.url = url + + ######################################################################## + def getComponent(self): + return self.component + + ######################################################################## + def isResolved(self): + return self.resolved + + ######################################################################## + def startResolve(self,autoopen): + if self.smbdialog.appendToResolveQueue(self): + self.setBusyIcon(True) + self.autoopen = self.autoopen or autoopen + + ######################################################################## + def cancelResolve(self): + self.setBusyIcon(False) + self.autoopen = False + self.resolved = False + while self.childCount()!=0: + self.takeItem(self.firstChild()) + self.setOpen(False) + + ######################################################################## + def unresolve(self): + self.cancelResolve() + + ######################################################################## + def getMachineItem(self): + if self.getLevel()<=self.LEVEL_WORKGROUP: + return None + elif self.getLevel()==self.LEVEL_DIR: + return self.parent().getMachineItem() + else: + return self + + ######################################################################## + def _setIcon(self,depth): + if depth==self.LEVEL_ROOT or depth==self.LEVEL_WORKGROUP: + self.setPixmap(0,SmallIcon("network")) + elif depth==self.LEVEL_MACHINE: + self.setPixmap(0,SmallIcon("network_local")) + else: + self.setPixmap(0,SmallIcon("folder")) + + ######################################################################## + def setBusyIcon(self,on): + if on: + self.setPixmap(0,UserIcon("kde1")) + self.setPixmap(0,UserIcon("kde"+str(self.animationcounter+1))) + self.animationcounter += 1 + self.animationcounter %= 6 + else: + self._setIcon(self.depth()) + + ######################################################################## + def resolveComplete(self): + self.resolved = True + if self.childCount()==0: + self.setExpandable(False) + else: + if self.autoopen: + self.setOpen(True) + ######################################################################## + def getLevel(self): + if self.depth()>self.LEVEL_DIR: + return self.LEVEL_DIR + else: + return self.depth() + + ######################################################################## + # This is one of the more nasty pieces of code. It tries to select a given + # URL in the treeview. Opening and resolving the contents of URLs as neccessary + # while at the same time trying not have list everything on the network. + # Another wrinkle is that the treeview contains a level of workgroups while + # a given URL omits the workgroup a jumps directly to the machine name. + def selectURL(self,targeturl): + path = unicode(targeturl.path(-1)) + parts = [x for x in path.split("/") if x!=""] + if targeturl.hasHost(): + tmp = [targeturl.host()] + tmp.extend(parts) + parts = tmp + + if self.getLevel()==self.LEVEL_ROOT: + # Root item. + # We should first resolve our contents. the Workgroups. + if not self.resolved: + self.startResolve(True) + return self.OPEN_BUSY + else: + if len(parts)==0: + # The URL is really short, and is not selectable. + # So we just say that we couldn't resolve/select it. + return self.OPEN_SUCCESS_INVALID + else: + # OK, the url has some more components. Ask each of the Workgroup items + # to help resolve it. + kid = self.firstChild() + while kid is not None: + rc = kid.selectURL(targeturl) + if rc==self.OPEN_SUCCESS or rc==self.OPEN_SUCCESS_INVALID: + kid.setOpen(True) + return rc + elif rc==self.OPEN_BUSY: + return rc + kid = kid.nextSibling() + return self.OPEN_FAIL + elif self.getLevel()==self.LEVEL_WORKGROUP: + # Workgroup level + if not self.resolved: + self.startResolve(False) + return self.OPEN_BUSY + else: + # Find a child named after the next part of the URL path. + kid = self.firstChild() + partname = parts[0].lower() + while kid is not None: + if kid.getComponent()==partname: + self.setOpen(True) + return kid.selectURL(targeturl) + kid = kid.nextSibling() + return self.OPEN_FAIL + elif self.getLevel()==self.LEVEL_MACHINE: + # Machine level + if len(parts)==1: + # The URL is successfully resolved but is not selectable! + return self.OPEN_SUCCESS_INVALID + else: + # Share level + if len(parts)==self.depth()-1: + self.smbdialog.treeview.setSelected(self,True) + return self.OPEN_SUCCESS + + if not self.resolved: + self.startResolve(True) + return self.OPEN_BUSY + else: + # Find a child item that matches the next part of the URL path. + kid = self.firstChild() + partname = parts[self.depth()-1].lower() + while kid is not None: + if kid.getComponent()==partname: + return kid.selectURL(targeturl) + kid = kid.nextSibling() + return self.OPEN_FAIL diff --git a/mountconfig/SimpleCommandRunner.py b/mountconfig/SimpleCommandRunner.py new file mode 100644 index 0000000..d533563 --- /dev/null +++ b/mountconfig/SimpleCommandRunner.py @@ -0,0 +1,69 @@ +########################################################################### +# SimpleCommandRunner.py - description # +# ------------------------------ # +# begin : Tue May 17 2005 # +# copyright : (C) 2005 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### +from qt import * +from kdecore import * +import locale + +debug = False +#debug = True + + +class SimpleCommandRunner(QObject): + ######################################################################## + def __init__(self): + QObject.__init__(self) + + ######################################################################## + def run(self,cmdlist,STDOUT_only=False): + """Run the given command and return the result. + + Keyword arguments: + cmdlist - Command and arguments. Given as a list of strings. The first item is + the executable. + STDOUT_only - Do not return STDERR in the output stream. + + Returns a tuple (rc,output). rc is the numeric return code from + the command, or None if the command couldn't be started. output + is the output from stdout and stderr. + """ + global debug + if debug: print cmdlist + self.STDOUT_only = STDOUT_only + self.output = u"" + proc = KProcess() + proc.setEnvironment("LANG","US") + proc.setEnvironment("LC_ALL","US") + self.connect(proc,SIGNAL("receivedStdout(KProcess *,char *,int)"),self.slotStdout) + self.connect(proc,SIGNAL("receivedStderr(KProcess *,char *,int)"),self.slotStderr) + proc.setArguments(cmdlist) + rc = None + if proc.start(proc.Block,proc.AllOutput)==True: + if proc.normalExit(): + rc = proc.exitStatus() + return (rc,self.output) + + ######################################################################## + def slotStdout(self,proc,buffer,buflen): + global debug + if debug: print "slotStdout() |"+buffer+"|" + self.output += buffer.decode(locale.getpreferredencoding()) + + ######################################################################## + def slotStderr(self,proc,buffer,buflen): + global debug + if debug: print "slotStderr() |"+buffer+"|" + if not self.STDOUT_only: + self.output += buffer.decode(locale.getpreferredencoding()) diff --git a/mountconfig/fuser.py b/mountconfig/fuser.py new file mode 100644 index 0000000..d898b37 --- /dev/null +++ b/mountconfig/fuser.py @@ -0,0 +1,299 @@ +#!/usr/bin/python +########################################################################### +# fuser.py - description # +# ------------------------------ # +# begin : Wed Jun 15 2005 # +# copyright : (C) 2005-2006 by Sebastian Kuegler # +# email : sebas@vizZzion.org # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### +""" +TODO: +- Fix running standalone: + * KCmdLineArgs stuff. +""" + +import sys +import os +from qt import * +from kdeui import * +#import kdedesigner +from fuser_ui import * +from SimpleCommandRunner import * + +standalone = __name__ == "__main__" + +class FileProcess(QListViewItem): + """ A FileProcess is simply one line from lsof, one filedescriptor that's in use + by a process represented as a listviewitem in the lsof processtable. """ + + # Available signals. + signals = { + "TERM":15, + "KILL":9 } + + # Column names mapping. + cols = { + "pname":0, + "pid":1, + "powner":2, + "pfile":3 } + + def __init__(self,parent,pid,isparent=False): + QListViewItem.__init__(self,parent) + self.setPid(pid) + self.isparent = isparent + self.pfile = "" + self.pix = None + + def setPid(self,pid): + self.pid = pid + + def setName(self,pname): + self.pname = pname + + def setOwner(self,powner): + self.powner = powner + + def setFile(self,pfile): + self.pfile = pfile + + def setPixmaps(self,pix): + """ Eats a dict with pixmaps. """ + self.pix = pix + + def sendSignal(self,signal): + """ Parses a signal string representation or a signal number and sends it to + the process.""" + if not self.isparent: + print "Item is not a process, only a filedescriptor." + return + try: + signal_int = int(signal) + except ValueError: + try: + signal_int = self.signals[signal] + except IndexError: + print "No known signal received ", signal + return False + try: + rc = os.kill(int(self.pid),signal_int) # TODO: Catch OSError + except OSError, message: + print "OSError: Couldn't %s %s %s" % (signal,self.pname,self.pid) + print message + if not rc: + print "Successfully sent signal ", signal_int, " to process ", self.pid + return True + print "Signal %i didn't succeed" % signal_int + return False + + def fillColumns(self): + """ Writes strings into columns once an entry is completed. """ + if self.isparent: + self.setText(self.cols["pid"],self.pid) + self.setText(self.cols["pname"],self.pname) + self.setText(self.cols["powner"],self.powner) + self.setPixmap(0,self.pix["exec"]) + self.setPixmap(1,self.pix["pid"]) + self.setPixmap(2,self.pix["owner"]) + else: + self.setText(self.cols["pfile"],self.pfile) + self.setPixmap(3,self.pix["file"]) + +######################################################################################################## +class FUser(FUserUI): + """ done() / result() return 0 on successful umount and 1 if cancelled. """ + + def __init__(self,device,parentdialog=None,lsof_bin='/usr/sbin/lsof',kapp=None): + FUserUI.__init__(self,parentdialog,name = None,modal = 0,fl = 0) + self.device = device + self.fileprocesses = [] + self.lsof_bin = '/usr/sbin/lsof' + self.setLsof(lsof_bin) + self.setApp(kapp) + + self.processlist.clear() + self.processhidden = False + # We're having processes blocking umounting, show that. + self.umountbutton.setEnabled(False) + + self.explanationlabel.setText( + unicode(i18n("""The volume %s is in use and can not be disabled.<br> + <br> + The processes that are blocking %s are listed below. These processes must be closed + before %s can be disabled. + Killing a process may cause data loss! Make sure all work is saved before killing an + application. + """)) % (self.device,self.device,self.device)) + + self.connect(self.cancelbutton,SIGNAL("clicked()"),self.slotCancelButtonClicked) + self.connect(self.killbutton,SIGNAL("clicked()"),self.slotKillButtonClicked) + self.connect(self.killallbutton,SIGNAL("clicked()"),self.slotKillallButtonClicked) + self.connect(self.refreshbutton,SIGNAL("clicked()"),self.refreshProcesslist) + self.connect(self.processlist,SIGNAL("selectionChanged()"),self.slotSelectionChanged) + self.connect(self.umountbutton,SIGNAL("clicked()"),self.slotUmountButtonClicked) + + # TODO: Make optionsbutton resize dialog if processframe is hidden, hide Optionsbutton until then. + self.optionsbutton.hide() + self.readPixmaps() + self.warningimage.setPixmap(MainBarIcon("messagebox_warning")) + + # Delayed initialisation. + QTimer.singleShot(0,self.isMounted) + QTimer.singleShot(0,self.refreshProcesslist) + + def setApp(self,app): + """ We need a reference to the (K|Q)Application for certain things, e.g. setting + the MouseCursor. """ + self.app = app + + def setLsof(self,path): + """ Where's the lsof binary? """ + if os.path.isfile(path): + self.lsof_bin = path + else: + print path, " is not a valid binary, keeping %s", self.lsof_bin + + def readPixmaps(self): + self.pix = { + "exec": UserIcon("exec"), + "owner": UserIcon("user"), + "pid": UserIcon("tux"), + "file": UserIcon("file")} + + def refreshProcesslist(self): + """ Read lsof output and add the processdescriptors to the listview. """ + kapp = self.app + + kapp.setOverrideCursor(QCursor(Qt.BusyCursor)) + + self.processlist.clear() + rc, output = SimpleCommandRunner().run([self.lsof_bin,'-FpcLn',self.device],True) + procs = output.split() + + self.processes = [] + self.realprocesses = [] + for line in procs: + line = str(line) + type = line[0] + info = line[1:] + + if type is "p": + pid = info + parentproc = FileProcess(self.processlist,pid,True) + self.processes.append(parentproc) + self.realprocesses.append(parentproc) + parentproc.setPixmaps(self.pix) + files = 0 + + if type == "c": + pname = info + parentproc.setName(pname) + + if type == "L": + powner = info + parentproc.setOwner(powner) + + if type == "n": + pfile = info + childproc = FileProcess(parentproc,pid) + self.processes.append(childproc) + childproc.setPixmaps(self.pix) + childproc.setFile(pfile) + childproc.setOwner(powner) + childproc.setName(pname) + if files == 0: + parentproc.fillColumns() + files += 1 + childproc.fillColumns() + + kapp.restoreOverrideCursor() + + # Enable / disable buttons which are (in)appropriate. + self.killallbutton.setEnabled(len(self.realprocesses)!=0) + self.killbutton.setEnabled(len(self.realprocesses)!=0) + self.umountbutton.setEnabled(len(self.realprocesses)==0) + if self.processlist.selectedItem() == None: + self.killbutton.setEnabled(False) + + def isMounted(self): + rc,output = SimpleCommandRunner().run(["/bin/mount"],False) + mounts = [] + for line in output.split('\n'): + try: + mounts.append(line.split()[0]) + except IndexError: + pass + ismounted = self.device in mounts + self.umountbutton.setEnabled(ismounted) + return ismounted + + def slotCancelButtonClicked(self): + self.done(1) + + def slotKillButtonClicked(self): + try: + self.processlist.selectedItem().sendSignal("KILL") + self.refreshProcesslist() + except AttributeError: + print "No killable item selected." + + def slotKillallButtonClicked(self): + for process in self.realprocesses: + process.sendSignal("KILL") + self.refreshProcesslist() + + def slotOptionsButtonCLicked(self): + self.processhidden = not self.processhidden + self.processframe.setHidden(self.processhidden) + + def slotSelectionChanged(self): + """ Check if item is a process or a file, disable killbutton for children. """ + selected = self.processlist.selectedItem() + if not selected.isparent: + self.killbutton.setEnabled(False) + else: + self.killbutton.setEnabled(True) + + def slotUmountButtonClicked(self): + SimpleCommandRunner + rc, output = SimpleCommandRunner().run(['/bin/umount',self.device]) + if rc == 0: + print "%s successfully unmounted." % self.device + # Close dialog and return 0 - sucessfully umounted. + self.done(0) + else: + print "Unmounting %s failed: %s" % (self.device,output[:-1]) + self.isMounted() + +################################################################################################ +if standalone: + device = "/dev/hda1" + print 'Device is ', device + + cmd_args = KCmdLineArgs.init(sys.argv, "FUser", + "A graphical frontend to fuser, but without using it :-)", "0.2") + + # ---------------------------------------------------------------------------- + # FIXME: All the arg-parsing stuff does not work yet since I don't understand KCmdLineArgs. + options = [("device <device>", "Device to umount")] + KCmdLineArgs.addCmdLineOptions(options) + args = KCmdLineArgs.parsedArgs() + # print args.count() + # ---------------------------------------------------------------------------- + + kapp = KApplication() + KGlobal.iconLoader().addAppDir("guidance") + fuserapp = FUser(device) + + fuserapp.setApp(kapp) + kapp.setMainWidget(fuserapp) + fuserapp.show() + kapp.exec_loop() diff --git a/mountconfig/fuser_ui.ui b/mountconfig/fuser_ui.ui new file mode 100644 index 0000000..4499f93 --- /dev/null +++ b/mountconfig/fuser_ui.ui @@ -0,0 +1,352 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>FUserUI</class> +<comment>Python:from kdeui import * +Python:from kdecore import *</comment> +<author>Sebastian Kuegler</author> +<widget class="QDialog"> + <property name="name"> + <cstring>Process</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>546</width> + <height>536</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>400</width> + <height>250</height> + </size> + </property> + <property name="baseSize"> + <size> + <width>400</width> + <height>250</height> + </size> + </property> + <property name="caption"> + <string>Error: Volume in use</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>warningimage</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="pixmap"> + <pixmap></pixmap> + </property> + <property name="scaledContents"> + <bool>false</bool> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>explanationlabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>The volume $VOLUME is in use and can not be disabled. + +The processes that are blocking $VOLUME are listed below. These processes must be closed before $VOLUME can be disabled. +Killing a process may cause data loss. Make sure all work is saved before killing an application.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </hbox> + </widget> + <widget class="QFrame"> + <property name="name"> + <cstring>processframe</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>5</number> + </property> + <property name="spacing"> + <number>4</number> + </property> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListView"> + <column> + <property name="text"> + <string>Process</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Process ID</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Owner</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>File</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>processlist</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>1</horstretch> + <verstretch>1</verstretch> + </sizepolicy> + </property> + <property name="resizePolicy"> + <enum>Manual</enum> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="showSortIndicator"> + <bool>true</bool> + </property> + <property name="rootIsDecorated"> + <bool>true</bool> + </property> + <property name="resizeMode"> + <enum>LastColumn</enum> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout11</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>killallbutton</cstring> + </property> + <property name="text"> + <string>Kill all listed processes</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>listbuttonspacer</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>184</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>killbutton</cstring> + </property> + <property name="text"> + <string>Kill process</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>refreshbutton</cstring> + </property> + <property name="text"> + <string>Refresh list</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout12</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>optionsbutton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>>>> Options</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>bottombuttonspacer</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>280</width> + <height>16</height> + </size> + </property> + </spacer> + <widget class="KPushButton"> + <property name="name"> + <cstring>umountbutton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Disable volume</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>cancelbutton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<pixmapfunction>QPixmap</pixmapfunction> +<layoutdefaults spacing="6" margin="6"/> +<includehints> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/mountconfig/mountconfig.desktop b/mountconfig/mountconfig.desktop new file mode 100644 index 0000000..e0fa311 --- /dev/null +++ b/mountconfig/mountconfig.desktop @@ -0,0 +1,51 @@ +[Desktop Entry] +Name=Disk & Filesystems +Name[da]=Disk & Filesystemer +Name[el]=Δίσκοι & συστήματα αρχείων +Name[es]=Disco y sistemas de archivos +Name[et]=Ketas ja failisüsteemid +Name[it]=Dischi e filesystem +Name[ja]=ディスクとファイルシステム +Name[nl]=Schijven en bestandssystemen +Name[pt]=Disco & Sistemas de Ficheiros +Name[pt_BR]=Disco & Sistemas de Arquivos +Name[sr]=Диск и фајл-системи +Name[sr@Latn]=Disk i fajl-sistemi +Name[sv]=Disk- och filsystem +Name[xx]=xxDisk & Filesystemsxx +name[en_GB]=Disk & Filesystems +Comment=Disk & Filesystem Configuration +Comment[el]=Ρυθμίσεις δίσκων & συστημάτων αρχείων +Comment[es]=Configuración del disco y sistema de archivos +Comment[et]=Ketta ja failisüsteemi seadistamine +Comment[it]=Configurazione di dischi e filesystem +Comment[ja]=ディスクとファイルシステムの設定 +Comment[nl]=Schijven en bestandssystemen instellen +Comment[pt]=Configuração do Disco & Sistema de Ficheiros +Comment[pt_BR]=Configuração de Disco e Sistemas de Arquivos +Comment[sr]=Подешавање диска и фајл-система +Comment[sr@Latn]=Podešavanje diska i fajl-sistema +Comment[sv]=Disk och filsysteminitierníng +Comment[xx]=xxDisk & Filesystem Configurationxx +Icon=disksfilesystems.png +Encoding=UTF-8 +X-KDE-ModuleType=Library +X-KDE-Library=mountconfig +X-KDE-FactoryName=mountconfig +X-KDE-RootOnly=true +Type=Application +Exec=kcmshell System/mountconfig +Categories=Qt;KDE;X-KDE-settings-system; +GenericName=Mount Point Editor +GenericName[el]=Επεξεργαστής σημείων προσάρτησης +GenericName[es]=Editor del punto de montaje +GenericName[et]=Ühenduspunktide redaktor +GenericName[it]=Editor dei punti di montaggio +GenericName[ja]=マウントポイントエディタ +GenericName[nl]=Aankoppelpunten bewerken +GenericName[pt]=Editor de Pontos de Montagem +GenericName[pt_BR]=Editor de Pontos de Montagem +GenericName[sr]=Уређивач тачки монтирања +GenericName[sr@Latn]=Uređivač tački montiranja +GenericName[sv]=Editor för monteringspunkter +GenericName[xx]=xxMount Point Editorxx diff --git a/mountconfig/mountconfig.py b/mountconfig/mountconfig.py new file mode 100755 index 0000000..1f31f13 --- /dev/null +++ b/mountconfig/mountconfig.py @@ -0,0 +1,3303 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +########################################################################### +# mountconfig.py - description # +# ------------------------------ # +# begin : Fri Nov 30 2003 # +# copyright : (C) 2003 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### + +from qt import * +from kdeui import * +from kdecore import * +from kfile import * +from kio import * +import sys +import os +import os.path +from types import StringType,UnicodeType +import pwd +import grp +import math +import locale +import codecs +import subprocess +import MicroHAL +from SMBShareSelectDialog import * +from SimpleCommandRunner import * +from fuser import * +import sizeview + +programname = "Disk & Filesystem Configuration" +version = "0.8.0" + +# Are we running as a separate standalone application or in KControl? +standalone = __name__=='__main__' + +# Running as the root user or not? +isroot = os.getuid()==0 +allowuuid = True +allowlabel = True + + +""" +Universal Options +----------------- + +async/sync +atime/noatime +auto/noauto +dev/nodev +exec/noexec +ro/rw +suid/nosuid +dirsync +nouser/user/users + +defaults =>rw, suid, dev, exec, auto, nouser, and async. + +Automatically set +================= +_netdev +The filesystem resides on a device that requires network access (used to +prevent the system from attempting to mount these filesystems until the +network has been enabled on the system). + +remount +Attempt to remount an already-mounted file system. This is commonly used +to change the mount flags for a file system, especially to make a readonly +file system writeable. It does not change device or mount point. + +Supported filesystems +--------------------- +nfs +ext2 +ext3 +reiserfs +vfat +ntfs +udf +iso9660 +supermount +reiser4 +xfs +jfs +hfs +hfsplus + +cifs (replacement for smbfs) +auto + +swap + +proc +sysfs +usbdevfs +procbususb + +TODO +---- +* SMB finished the connection username nad password fields. +* SMB entry: finished writing the config. +* SMBSelector: setting the username and password. + +""" + +############################################################################ +class UserComboBox(KComboBox): + def __init__(self,parent,name=None): + KComboBox.__init__(self,parent,name) + tmplist = [] + users = pwd.getpwall() + for user in users: + uid = int(user[2]) + username = user[4] + tmplist.append( (int(uid),"%s (%s)" % (username,uid)) ) + tmplist.sort(lambda a,b: cmp(a[1],b[1])) + self.userlist = [] + for user in tmplist: + self.insertItem(user[1]) + self.userlist.append(user[0]) + + ######################################################################## + def setUID(self,uid): + if uid in self.userlist: + self.setCurrentItem(self.userlist.index(int(uid))) + return True + else: + return False + + ######################################################################## + def UID(self): + return self.userlist[self.currentItem()] + +############################################################################ +class GroupComboBox(KComboBox): + def __init__(self,parent,name=None): + KComboBox.__init__(self,parent,name) + self.grouplist = [] + groups = grp.getgrall() + tmplist = [] + for group in groups: + gid = group[2] + groupname = group[0] + tmplist.append( (int(gid),"%s (%s)" % (groupname,gid)) ) + tmplist.sort(lambda a,b: cmp(a[1],b[1])) + self.grouplist = [] + for group in tmplist: + self.insertItem(group[1]) + self.grouplist.append(group[0]) + + ######################################################################## + def setGID(self,gid): + if gid in self.grouplist: + self.setCurrentItem(self.grouplist.index(int(gid))) + return True + else: + return False + + ######################################################################## + def GID(self): + return self.grouplist[self.currentItem()] + + +############################################################################ +class MountEntryExt(object): + use_as_device = "devicenode" # Can be one of "devicenode", "uuid" or "label" + showdevice = True + showlabel = False + showuuid = False + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + if base==None: + self.device = unicode(i18n("<device>")) + self.mountpoint = unicode(i18n("<mount point>")) + self.mounttype = 'ext2' + self.uuid = "" + self.label = "" + self.extraoptions = "noauto" + self.fs_freq = 0 + self.fs_passno = 0 + self.enabled = False + self.managed = False + self.device_string = "" + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + parts = base.split() + + device_ref = MountEntry.decodeMountEntryString(parts[0]) + self.uuid = "" + self.label = "" + if device_ref.startswith("UUID="): + self.uuid = device_ref[5:] + self.setUseAsDevice("uuid") + mapped_device = microhal.getDeviceByUUID(self.uuid) + if mapped_device is not None: + self.device = mapped_device.getDev() + else: + self.device = "" + try: + self.label = mapped_device.getLabel() + except AttributeError: + pass + elif device_ref.startswith("LABEL="): + self.label = device_ref[6:] + self.setUseAsDevice("label") + mapped_device = microhal.getDeviceByLabel(self.label) + if mapped_device is not None: + self.device = mapped_device.getDev() + else: + self.device = "" + else: + self.device = device_ref + + self.mountpoint = MountEntry.decodeMountEntryString(parts[1]) + self.mounttype = MountEntry.decodeMountEntryString(parts[2]) + self.extraoptions = MountEntry.decodeMountEntryString(parts[3]) + self.fs_freq = int(parts[4]) + self.fs_passno = int(parts[5]) + self.enabled = False + + options = self.extraoptions.split(",") + self.managed = "managed" in options + try: + options.remove("managed") + except ValueError: + pass + self.extraoptions = ",".join(options) + + else: + # This is a new entry, but it's based on another one. + self.device = base.device + self.mountpoint = base.mountpoint + self.mounttype = base.mounttype + self.extraoptions = base.extraoptions + self.fs_freq = base.fs_freq + self.fs_passno = base.fs_passno + self.uuid = base.uuid + self.enabled = base.enabled + self.managed = False + self.iconname = self.getIconName() + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExt() + # FIXME: use "newobject = self.__class__()" and get rid of the newobject parameter. + newobject.device = self.device + newobject.mountpoint = self.mountpoint + newobject.mounttype = self.mounttype + newobject.use_as_device = self.use_as_device + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + newobject.extraoptions = self.extraoptions + newobject.fs_freq = self.fs_freq + newobject.fs_passno = self.fs_passno + newobject.enabled = self.enabled + newobject.uuid = self.uuid + newobject.label = self.label + return newobject + + ######################################################################## + def cleanup(self): + # This method is called after the entry has been removed from the + # mounttable. + pass + + ######################################################################## + def setMountType(self,mounttypename): self.mounttype = mounttypename + + ######################################################################## + def isFileSystemAvailable(self): + return microhal.isSupportedFileSystem(self.mounttype) + + def getDevice(self): return self.device + def setDevice(self,device): self.device = device + def setUseAsDevice(self,use_as): self.use_as_device = use_as + def getUseAsDevice(self): return self.use_as_device + def setUUID(self,uuid): self.uuid = uuid + def setLabel(self,label): self.label = label + def getMountPoint(self): return self.mountpoint + def setMountPoint(self,mountpoint): self.mountpoint = mountpoint + def getExtraOptions(self): return self.extraoptions + def setExtraOptions(self,extraoptions): self.extraoptions = extraoptions + def getFSFreq(self): return self.fs_freq + def setFSFreq(self,fs_freq): self.fs_freq = fs_freq + def getFSPassno(self): return self.fs_passno + def setFSPassno(self,fs_passno): self.fs_passno = fs_passno + def isManaged(self): return self.managed + + def getUUID(self): + if not self.uuid: + return "" + return self.uuid + + def getLabel(self): + try: + if not self.label: + return "" + return self.label + except AttributeError: + return "" + + def getDeviceString(self): + if self.getUseAsDevice() == "label": + if self.label != "": + return MountEntry.encodeMountEntryString("LABEL="+self.label) + else: + print "No Label set, preventing you from shooting yourself in the foot" + elif self.getUseAsDevice() == "uuid": + if self.uuid != "": + return "UUID="+self.uuid + return MountEntry.encodeMountEntryString("UUID="+self.uuid) + else: + print "No UUID set, preventing you from shooting yourself in the foot" + return MountEntry.encodeMountEntryString(self.device) + + ######################################################################## + def getName(self): + if os.path.basename(self.device).startswith("fd"): + return "Floppy" + else: + return self.mountpoint + + ######################################################################## + def getIconName(self): + if self.device is not None and os.path.basename(self.device).startswith("fd"): + return "hi16-floppy" + else: + return "hi16-blockdevice" + + ######################################################################## + def updateStatus(self,mtablist): + self.enabled = self.mountpoint in mtablist + + ######################################################################## + def getFstabOptions(self): + if self.extraoptions!="": + return self.extraoptions.split(",") + else: + return [] + + ######################################################################## + def getFstabLine(self): + # Construct the options field. + _options = self.getFstabOptions() + options = [] + # Remove whitespace and dupes + for o in _options: + if o.strip() not in options: + options.append(o.strip()) + + return self.getDeviceString() + \ + u" " + MountEntry.encodeMountEntryString(self.mountpoint.replace("%20","\040")) + \ + u" " + MountEntry.encodeMountEntryString(self.mounttype) + \ + u" " + MountEntry.encodeMountEntryString(u",".join(options)) + \ + u" " + unicode(self.fs_freq) + u" " + unicode(self.fs_passno) + + ######################################################################## + def getCategory(self): + return self.device + + ######################################################################## + def isEnabled(self): return self.enabled + + ######################################################################## + def enable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(["/bin/mount",self.mountpoint]) + finally: + self._setBusy(parentdialog,False) + if rc!=0: + self.handleMountFailure(parentdialog,rc,output,True) + + ######################################################################## + def disable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(["/bin/umount",self.mountpoint]) + finally: + self._setBusy(parentdialog,False) + if rc!=0: + self.handleMountFailure(parentdialog,rc,output,False) + + ######################################################################## + def handleMountFailure(self,parentdialog,rc,output,mount_action=True): + """ + Keyword arguments: + mount_action - True=enable, False=disable + """ + global kapp + if mount_action: + msg = i18n("An error occurred while enabling %1.\n\nThe system reported: %2").arg( \ + self.mountpoint).arg(output) + captionmsg = i18n("Unable to enable %1").arg(self.mountpoint) + else: + msg = i18n("An error occurred while disabling %1.\n\nThe system reported: %2").arg( + self.mountpoint).arg(output) + captionmsg = i18n("Unable to disable %1").arg(self.mountpoint) + + extramsg = unicode(i18n("Return code from mount was %1.\n").arg(rc)) + + if (rc & 1)!=0: + extramsg += unicode(i18n("\"incorrect invocation or permissions\"\n")) + if (rc & 2)!=0: + extramsg += unicode(i18n("\"system error (out of memory, cannot fork, no more loop devices)\"\n")) + if (rc & 4)!=0: + extramsg += unicode(i18n("\"internal mount bug or missing nfs support in mount\"\n")) + if (rc & 8)!=0: + extramsg += unicode(i18n("\"user interrupt\"\n")) + if (rc & 16)!=0: + extramsg += unicode(i18n("\"problems writing or locking /etc/mtab\"\n")) + if (rc & 32)!=0: + extramsg += unicode(i18n("\"mount failure\"\n")) + if (rc & 64)!=0: + extramsg += unicode(i18n("\"some mount succeeded\"\n")) + + in_use = False + if not mount_action: + # Use lsof to find out what is blocking the device. + lsof_bin = '/usr/bin/lsof' + rc, output = SimpleCommandRunner().run([lsof_bin,'-FncL',str(self.mountpoint)]) + if rc==0: + # Check if there is one or more processes using the device. + in_use = len(output.split())>3 + if in_use: + # Start fuser.py which lists open filedescriptors on device and offers to get + # rid of them. + fuser = FUser(str(self.mountpoint),None,lsof_bin,kapp) + fuser.exec_loop() + in_use_message = "" + if fuser.result() != 0: + in_use_message = unicode(i18n("Unmounting %1 failed or was cancelled.").arg(self.device)) + extramsg += in_use_message + else: + extramsg += unicode(i18n("(none)")) + + if not in_use: + KMessageBox.detailedSorry(parentdialog, msg, extramsg, captionmsg) + + ######################################################################## + def _setBusy(self,parentdialog,flag): + global kapp + if flag: + kapp.setOverrideCursor( QCursor(Qt.WaitCursor) ) + parentdialog.setEnabled(False) + + # It is necessary to process some of the events in the event queue. + # Otherwise the user won't see that the window is disabled. + # ( setEnabled() here above doesn't redraw the window immediately. + # Redrawing is done via the event queue.) + kapp.processEvents() + else: + parentdialog.setEnabled(True) + kapp.restoreOverrideCursor() + +############################################################################ +class MountEntryExtCommonUnix(MountEntryExt): + + USERMOUNT_NO = 0 + USERMOUNT_ONE = 1 + USERMOUNT_ANY = 2 + USERMOUNT_OWNER = 3 + + ######################################################################## + # Base can be either a fstab format line of text, or another MountEntry + # object. + def __init__(self,base=None): + super(MountEntryExtCommonUnix,self).__init__(base) + + if isinstance(base,MountEntryExtCommonUnix): + # Being initalised from an existing object. + # Only mess with objects + self.atime = base.atime + self.auto = base.auto + self.writeable = base.writeable + self.usedevpoints = base.usedevpoints + self.showlabel = True + self.showuuid = True + self.allowexecutables = base.allowexecutables + self.allowsuid = base.allowsuid + self.allowusermount = base.allowusermount + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + options = self.extraoptions.split(",") + + self.atime = True + if "noatime" in options: + self.atime = False + self.auto = True + if "noauto" in options: + self.auto = False + self.writeable = True + if "ro" in options: + self.writeable = False + self.usedevpoints = True + if "nodev" in options: + self.usedevpoints = False + self.allowexecutables = True + if "noexec" in options: + self.allowexecutables = False + self.allowsuid = True + if "nosuid" in options: + self.allowsuid = False + self.allowusermount = self.USERMOUNT_NO + if "user" in options: + self.allowusermount = self.USERMOUNT_ONE + if "users" in options: + self.allowusermount = self.USERMOUNT_ANY + if "owner" in options: + self.allowusermount = self.USERMOUNT_OWNER + + self.showlabel = True + self.showuuid = True + + for x in ["noatime","atime","auto","noauto","dev","nodev","nouser", \ + "owner","users","user","suid","nosuid","exec","noexec","rw","ro"]: + try: + options.remove(x) + except ValueError: + pass + + self.extraoptions = ",".join(options) + + else: + # Set some sane defaults. + self.showlabel = True + self.showuuid = True + self.atime = True + self.auto = False + self.writeable = True + self.usedevpoints = False + self.allowexecutables = False + self.allowsuid = False + self.allowusermount = self.USERMOUNT_NO + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtCommonUnix() + super(MountEntryExtCommonUnix,self).copy(newobject) + newobject.atime = self.atime + newobject.auto = self.auto + newobject.use_as_device = self.use_as_device + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + newobject.uuid = self.uuid + newobject.label = self.label + newobject.writeable = self.writeable + newobject.usedevpoints = self.usedevpoints + newobject.allowexecutables = self.allowexecutables + newobject.allowsuid = self.allowsuid + newobject.allowusermount = self.allowusermount + return newobject + + ######################################################################## + def getFstabOptions(self): + options = [] + + # These options must appear before the others. 'user', according to the + # mount man page implies 'noexec' too, BUT the noexec can be overridden + # by specifying 'exec' after the 'user' keyword. Therefore 'exec' etc + # must come after 'user', 'users' and friends. + options.append(['nouser','user','users','owner'][self.allowusermount]) + + super_options = super(MountEntryExtCommonUnix,self).getFstabOptions() + options.extend(super_options) + + options.append(['noatime','atime'][self.atime]) + options.append(['noauto','auto'][self.auto]) + options.append(['ro','rw'][self.writeable]) + options.append(['nodev','dev'][self.usedevpoints]) + options.append(['noexec','exec'][self.allowexecutables]) + options.append(['nosuid','suid'][self.allowsuid]) + return options + + ######################################################################## + # atime/noatime + def getAtime(self): return self.atime + def setAtime(self,val): self.atime = val + # auto/noauto + def getMountAtBoot(self): return self.auto + def setMountAtBoot(self,val): self.auto = val + # ro/rw + def getWritable(self): return self.writeable + def setWritable(self,val): self.writeable = val + # dev, nodev + def getUseDevPoints(self): return self.usedevpoints + def setUseDevPoints(self,val): self.usedevpoints = val + # exec/noexec + def getAllowExecutables(self): return self.allowexecutables + def setAllowExecutable(self,val): self.allowexecutables = val + # suid/nosuid + def getSUID(self): return self.allowsuid + def setSUID(self,val): self.allowsuid = val + # nouser/user/users/owner + def setAllowUserMount(self,val): self.allowusermount = val + def getAllowUserMount(self): return self.allowusermount + + +############################################################################ +# Common unix filesystems, but for local hard disks. i.e. partitions. +class MountEntryExtCommonUnixLocal(MountEntryExtCommonUnix): + ######################################################################## + def __init__(self,base=None): + super(MountEntryExtCommonUnixLocal,self).__init__(base) + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtCommonUnixLocal() + super(MountEntryExtCommonUnixLocal,self).copy(newobject) + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + return newobject + + +############################################################################ +class MountEntryExtAlien(MountEntryExt): + + USERMOUNT_NO = 0 + USERMOUNT_ONE = 1 + USERMOUNT_ANY = 2 + USERMOUNT_OWNER = 3 + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + super(MountEntryExtAlien,self).__init__(base) + + if isinstance(base,MountEntryExtAlien): + self.uid = base.uid + self.gid = base.gid + self.label = base.label + self.writeable = base.writeable + self.auto = base.auto + self.allowusermount = base.allowusermount + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + self.uid = 0 + self.gid = 0 + options = self.extraoptions.split(",") + newoptions = [] + for line in options: + if line.startswith("uid="): + try: + self.uid = int(line[4:]) + except ValueError: + self.uid = 0 + elif line.startswith("gid="): + try: + self.gid = int(line[4:]) + except ValueError: + self.gid = 0 + else: + # We hang on to unknown options for later. + newoptions.append(line) + options = newoptions + + self.writeable = True + if "ro" in options: + self.writeable = False + self.auto = True + if "noauto" in options: + self.auto = False + self.allowusermount = self.USERMOUNT_NO + if "user" in options: + self.allowusermount = self.USERMOUNT_ONE + if "users" in options: + self.allowusermount = self.USERMOUNT_ANY + if "owner" in options: + self.allowusermount = self.USERMOUNT_OWNER + + for x in ["noatime","atime","auto","noauto","dev","nodev","nouser", \ + "owner","users","user","suid","nosuid","exec","noexec","rw", \ + "ro"]: + try: + options.remove(x) + except ValueError: + pass + self.extraoptions = ",".join(options) + + else: + self.uid = 0 + self.gid = 0 + self.writeable = False + self.auto = False + self.allowusermount = self.USERMOUNT_NO + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtAlien() + super(MountEntryExtAlien,self).copy(newobject) + newobject.uid = self.uid + newobject.gid = self.gid + newobject.use_as_device = self.use_as_device + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + newobject.writeable = self.writeable + newobject.auto = self.auto + newobject.allowusermount = self.allowusermount + return newobject + + ######################################################################## + def getFstabOptions(self): + # Construct the options field. + options = super(MountEntryExtAlien,self).getFstabOptions() + options.append('uid='+unicode(self.uid)) + options.append('gid='+unicode(self.gid)) + options.append(['noauto','auto'][self.auto]) + options.append(['ro','rw'][self.writeable]) + options.append(['nouser','user','users','owner'][self.allowusermount]) + return options + + ######################################################################## + def getUID(self): return self.uid + def setUID(self,val): self.uid = val + def getGID(self): return self.gid + def setGID(self,val): self.gid = val + + # ro/rw + def getWritable(self): return self.writeable + def setWritable(self,val): self.writeable = val + # auto/noauto + def getMountAtBoot(self): return self.auto + def setMountAtBoot(self,val): self.auto = val + # nouser/user/users/owner + def setAllowUserMount(self,val): self.allowusermount = val + def getAllowUserMount(self): return self.allowusermount + +############################################################################ +class MountEntryExtVFAT(MountEntryExtAlien): + def __init__(self,base=None): + super(MountEntryExtVFAT,self).__init__(base) + + if isinstance(base,MountEntryExtVFAT): + self.suppresspermissionerrors = base.suppresspermissionerrors + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + options = self.extraoptions.split(",") + self.suppresspermissionerrors = "quiet" in options + try: + options.remove("quiet") + except ValueError: + pass + self.extraoptions = ",".join(options) + + else: + self.suppresspermissionerrors = False + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtVFAT() + super(MountEntryExtVFAT,self).copy(newobject) + newobject.suppresspermissionerrors = self.suppresspermissionerrors + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + return newobject + + ######################################################################## + def getFstabOptions(self): + options = super(MountEntryExtVFAT,self).getFstabOptions() + if self.suppresspermissionerrors: + options.append('quiet') + return options + + def getSuppressPermissionErrors(self): return self.suppresspermissionerrors + def setSuppressPermissionErrors(self,val): self.suppresspermissionerrors = val + +############################################################################ +class MountEntryExtSMB(MountEntryExtAlien): + CREDENTIALSBASENAME = "/etc/fstab_smb_credentials_" + ######################################################################## + def __init__(self,base=None): + super(MountEntryExtSMB,self).__init__(base) + + if isinstance(base,MountEntryExtSMB): + self.username = base.username + self.password = base.password + self.credentialsfile = base.credentialsfile + + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + self.username = None + self.password = "" + self.credentialsfile = None + + newoptions = [] + options = self.extraoptions.split(",") + for line in options: + if line.startswith("username="): + self.username = line[9:] + elif line.startswith("password="): + self.password = line[9:] + elif line.startswith("credentials="): + self.credentialsfile = line[12:] + try: + fhandle = codecs.open(self.credentialsfile,'r',locale.getpreferredencoding()) + for line in fhandle.readlines(): + if line.startswith("username"): + self.username = line[8:].strip()[1:].strip() + elif line.startswith("password"): + self.password = line[8:].strip()[1:].strip() + fhandle.close() + + if not self.credentialsfile.startswith(self.CREDENTIALSBASENAME): + self.credentialsfile = None + + except IOError: + self.credentialsfile = None + + elif line=="guest": + pass + else: + # We hang on to unknown options for later. + newoptions.append(line) + options = newoptions + + if self.username == "": + self.username = None + + self.extraoptions = ",".join(options) + + else: + self.username = None + self.password = "" + self.credentialsfile = None + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtSMB() + super(MountEntryExtSMB,self).copy(newobject) + newobject.username = self.username + newobject.password = self.password + newobject.credentialsfile = self.credentialsfile + newobject.showlabel = self.showlabel + newobject.showdevice = self.showdevice + newobject.showuuid = self.showuuid + return newobject + + ######################################################################## + def cleanup(self): + if (self.credentialsfile is not None) and os.path.exists(self.credentialsfile) and os.path.isfile(self.credentialsfile): + os.remove(self.credentialsfile) + + ######################################################################## + def getIconName(self): + return "hi16-network" + + ######################################################################## + def getFstabOptions(self): + options = super(MountEntryExtSMB,self).getFstabOptions() + if self.username is None: + if (self.credentialsfile is not None) and os.path.exists(self.credentialsfile) and os.path.isfile(self.credentialsfile): + os.remove(self.credentialsfile) + options.append("guest") # This option should stop mount(8) from asking for a password. + else: + # Write out the credentials file + if self.credentialsfile is None: + i = 1 + while os.path.exists(self.CREDENTIALSBASENAME+unicode(i)): + i += 1 + self.credentialsfile = self.CREDENTIALSBASENAME+unicode(i) + fd = os.open(self.credentialsfile,os.O_WRONLY|os.O_CREAT,0600) + fhandle = os.fdopen(fd,'w') + fhandle.write((u"username = %s\npassword = %s\n" % (self.username,self.password)) + .encode(locale.getpreferredencoding(),'replace') ) + fhandle.close() + options.append(u"credentials="+self.credentialsfile) + return options + + ######################################################################## + def getUsername(self): return self.username + def setUsername(self,username): self.username = username + def getPassword(self): return self.password + def setPassword(self,password): self.password = password + +############################################################################ +class MountEntryExtSystem(MountEntryExt): + ######################################################################## + def __init__(self,base=None): + super(MountEntryExtSystem,self).__init__(base) + self.use_as_device = "devicenode" + self.label = "" + self.showuuid = False + self.showlabel = False + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtSystem() + super(MountEntryExtSystem,self).copy(newobject) + return newobject + + ######################################################################## + def getCategory(self): + return "system" + + def disable(self,parentdialog): + """ This shouldn't happen since system entries have the disable button disabled """ + msg = i18n("Disabling %1 is not supported.").arg(self.mountpoint) + extramsg = i18n("""Some system devices cannot be disabled because they are needed for \ + basic functionality of the operating system.""") + KMessageBox.detailedSorry(parentdialog,msg,extramsg,\ + i18n("Error occurred while disabling %1").arg(self.mountpoint)) + +############################################################################ +class MountEntryExtSwap(MountEntryExt): + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + super(MountEntryExtSwap,self).__init__(base) + + if isinstance(base,StringType) or isinstance(base,UnicodeType): + options = self.extraoptions.split(",") + try: + options.remove('defaults') + except ValueError: + pass + self.extraoptions = u",".join(options) + + ######################################################################## + def copy(self,newobject=None): + if newobject is None: + newobject = MountEntryExtSwap() + super(MountEntryExtSwap,self).copy(newobject) + return newobject + + ######################################################################## + def getFstabOptions(self): + options = super(MountEntryExtSwap,self).getFstabOptions() + if len(options)==0: + # Make sure there is at least one option in the list. + options.append('defaults') + return options + + ######################################################################## + def updateStatus(self,mtablist): + + this_device = self.device + if this_device is None: + # Find the device name by its UUID. + if self.uuid: + hal_device = microhal.getDeviceByUUID(self.uuid) + if self.label: + hal_device = microhal.getDeviceByLabel(self.label) + if hal_device is None: + self.enabled = False + return + this_device = hal_device.getDev() + + # If the device is a symlink, then grab the complete target. + if os.path.islink(this_device): + this_device = os.path.join(os.path.dirname(this_device),os.readlink(this_device)) + + fhandle = open("/proc/swaps") + lines = fhandle.readlines() + fhandle.close() + + try: del lines[0] + except IndexError: pass + + self.enabled = False + for line in lines: + parts = line.split() + if parts[0]==this_device: + self.enabled = True + return + + ######################################################################## + # Returns a list of command+arguments + def enable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(['/sbin/swapon',self.device]) + if rc!=0: + msg = i18n("An error occurred while enabling swap partition %1.\n\nThe system reported: %2").arg(self.device).arg(output) + KMessageBox.sorry(parentdialog,msg,\ + i18n("Error occurred while enabling swap partition %1").arg(self.device)) + finally: + self._setBusy(parentdialog,False) + + ######################################################################## + # Returns a list of command+arguments or None. + def disable(self,parentdialog): + self._setBusy(parentdialog,True) + try: + (rc,output) = SimpleCommandRunner().run(['/sbin/swapoff',self.device]) + if rc!=0: + msg = i18n("An error occurred while disabling swap partition %1.\n\nThe system reported: %2").arg(self.device).arg(output) + KMessageBox.sorry(parentdialog,msg,\ + i18n("Error occurred while disabling swap partition %1").arg(self.device)) + finally: + self._setBusy(parentdialog,False) + +############################################################################ +# This represents a mount entry. +# +# It also does a little trick with the MountEntryExt classes. MountEntry +# objects kind of 'change' class under your nose when they are set to +# different mount types. The handling of the different kinds of mount types +# is handled by MountEntryExt objects and subclasses. + +class MountEntry(object): + + MountTypes = { + 'proc' : (MountEntryExtSystem,i18n("proc")), + 'sysfs' : (MountEntryExtSystem,i18n("sysfs")), + 'rootfs' : (MountEntryExtSystem,i18n("rootfs")), + 'bdev' : (MountEntryExtSystem,i18n("bdev")), + 'sockfs' : (MountEntryExtSystem,i18n("sockfs")), + 'tmpfs' : (MountEntryExtSystem,i18n("tmpfs")), + 'shm' : (MountEntryExtSystem,i18n("shm")), + 'pipefs' : (MountEntryExtSystem,i18n("pipefs")), + 'devfs' : (MountEntryExtSystem,i18n("devfs - Device File System")), + 'devpts' : (MountEntryExtSystem,i18n("devpts")), + 'ramfs' : (MountEntryExtSystem,i18n("ramfs")), + 'auto' : (MountEntryExtCommonUnix,i18n("Automatic")), + 'usbdevfs' : (MountEntryExtSystem,i18n("usbdevfs")), + 'procbususb' : (MountEntryExtSystem,i18n("procbususb")), + 'usbfs' : (MountEntryExtSystem,i18n("usbfs")), + 'supermount' : (MountEntryExt,i18n("supermount")), + 'swap' : (MountEntryExtSwap,i18n("Swap - Linux Swap Space")), + + 'nfs' : (MountEntryExtCommonUnix,i18n("NFS - Network File System")), + 'cifs' : (MountEntryExtSMB,i18n("Windows File Sharing")), + + 'ext2' : (MountEntryExtCommonUnixLocal,i18n("Ext2 - Second Extended FS")), + 'ext3' : (MountEntryExtCommonUnixLocal,i18n("Ext3 - Third Extended FS")), + 'reiserfs' : (MountEntryExtCommonUnixLocal,i18n("ReiserFS")), + 'reiser4' : (MountEntryExtCommonUnixLocal,i18n("Reiser4")), + 'xfs' : (MountEntryExtCommonUnixLocal,i18n("XFS - SGI's journaling filesystem")), + 'hfs' : (MountEntryExtCommonUnixLocal,i18n("HFS - Apple's Hierarchical File System")), + 'hfsplus' : (MountEntryExtVFAT,i18n("HFS+ - Apple's modernized Hierarchical File System")), + 'jfs' : (MountEntryExtCommonUnixLocal,i18n("JFS - IBM's Journaled File System")), + 'vfat' : (MountEntryExtVFAT,i18n("VFAT - Microsoft FAT File Systems")), + 'ntfs' : (MountEntryExtVFAT,i18n("NTFS - NT File System")), + 'udf' : (MountEntryExtSystem,i18n("udf")), + 'iso9660' : (MountEntryExt,i18n("iso9660 - CD-ROM")), + } + + notInFstab = False + maydisable = True # Some entries, such as /proc can't be disabled. + + ######################################################################## + # Base can be either a fstab format line of text, of another MountEntry + # object. + def __init__(self,base=None): + try: + self.extensionObjects = {} + if base==None: + self.mounttype = 'auto' + elif isinstance(base,StringType) or isinstance(base,UnicodeType): + parts = base.split() + self.mounttype = parts[2] + # 'udf,iso9660' seems default for some devices in fstab, + # check if all listed filesystems are available, if yes set to 'auto'. + if len(self.mounttype.split(',')) > 1: + """ + # We could check here, but then we'd need a reference to MicroHAL. + #for m in self.mounttype.split(','): + #if m not in supported_fs: + # print "Filesystem ", m, "not supported by the kernel" + # break + """ + self.mounttype = "auto" + else: + # This is a new entry, but it's based on another one. + self.mounttype = base.mounttype + self.extension = self.MountTypes[self.mounttype][0](base) + self.extensionObjects[self.mounttype] = self.extension + except (KeyError,IndexError): + raise InvalidMountEntryError, u"Unable to parse mount entry:"+unicode(base) + + ######################################################################## + def getMountType(self): + return self.mounttype + + ######################################################################## + def setMountType(self,newtypename): + if newtypename not in self.extensionObjects: + try: + self.extensionObjects[newtypename] = self.MountTypes[newtypename][0](self.extension) + except KeyError: + raise NotImplementedError, "Unknown mounttype:"+newtypename + self.mounttype = newtypename + self.extension = self.extensionObjects[newtypename] + self.extension.setMountType(newtypename) + + ######################################################################## + def copy(self): + newentry = MountEntry() + newentry.mounttype = self.mounttype + newext = self.extension.copy() + newentry.use_as_device = self.use_as_device + newentry.showlabel = self.showlabel + newentry.showdevice = self.showdevice + newentry.showuuid = self.showuuid + newentry.extensionObjects[self.mounttype] = newext + newentry.extension = newext + return newentry + + ######################################################################## + def inPlaceCopyFrom(self,sourceentry): + self.extension.cleanup() + + tmpcopy = sourceentry.copy() + self.extensionObjects = tmpcopy.extensionObjects + self.mounttype = tmpcopy.mounttype + self.extension = tmpcopy.extension + + # Override the attribute lookup, set/get, to use the extension object + ######################################################################## + def __getattr__(self,name): + try: + return getattr(self.extension,name) + except AttributeError, a: + print a + + ######################################################################## +# FIXME +## def __setattr__(self,name,value): +## if 'extension' in self.__dict__: +## if name in self.extension.__dict__: +## setattr(self.extension,name,value) +## return +## self.__dict__[name] = value + + ######################################################################## + def getMountTypes(): + return MountEntry.MountTypes.keys() + getMountTypes = staticmethod(getMountTypes) + + ######################################################################## + def getMountTypeLongName(typename): + return MountEntry.MountTypes[typename][1] + getMountTypeLongName = staticmethod(getMountTypeLongName) + + ######################################################################## + def encodeMountEntryString(string): + newstring = u"" + for c in string: + if c==' ': + newstring += "\\040" + elif c=="\t": + newstring += "\\012" + elif c=='\\': + newstring += "\\134" + else: + newstring += c + return newstring + encodeMountEntryString = staticmethod(encodeMountEntryString) + + ######################################################################## + def decodeMountEntryString(string): + newstring = "" + while string!="": + if len(string)>=4 and string[0]=='\\' and isoct(string[1]) \ + and isoct(string[2]) and isoct(string[3]): + newstring += chr(64*(ord(string[1])-ord('0')) + \ + 8*(ord(string[2])-ord('0')) + (ord(string[3])-ord('0'))) + string = string[4:] + else: + newstring += string[0] + string = string[1:] + return newstring + decodeMountEntryString = staticmethod(decodeMountEntryString) + +############################################################################ +class MountEntryComment(MountEntry): + """ This represents a comment mount entry or generally something that we don't + understand (might be comment, might be fstab syntax we don't know, might be + a faulty line in there). We don't want to wipe that stuff out, but we can't + deal with it in a sensible way, so we keep it in a MountEntryComment, + exclude it from most operations, but will write it back to fstab afterwards + + As a result of that we only define the stuff that's necessary, namely saving + the fstab line and returning it when writing.""" + + ######################################################################## + def __init__(self,base=None): + self.row = base + + ######################################################################## + def getFstabLine(self): return self.row + +############################################################################ +def isoct(c): return c in '01234567' + +############################################################################ +class InvalidMountEntryError(Exception): + + ######################################################################## + def __init__(self,arg=None): + self.arg = arg + + ######################################################################## + def __str__(self): + return str(self.arg) + +############################################################################ +class MountTable(object): + + ######################################################################## + def __init__(self,fstab_filename,mtab_filename): + self.fstab_filename = fstab_filename + self.mtab_filename = mtab_filename + + self.entries = [] + self.allentries = [] + + # sysfs does not need an entry in fstab, so we add it even if it's not + # in there, it's mounted automatically anyway and shows up in mtab + sysfs_in_fstab = False + usbdevfs_in_fstab = False + + fhandle = codecs.open(self.fstab_filename,'r',locale.getpreferredencoding()) + for row in fhandle.readlines(): + row = row.strip('\n') # Carefully remove any trailing newline. + if row.strip().startswith("#") or row.strip()=="": + entry = MountEntryComment(row) + else: + try: + entry = MountEntry(row) + self.append(entry) + + if entry.getMountType() == "sysfs": + sysfs_in_fstab = True + if entry.getMountType() == "usbdevfs" or "procbususb": + usbdevfs_in_fstab = True + if entry.getMountType() == "proc": + entry.maydisable = False + except InvalidMountEntryError: + entry = MountEntryComment(row) + # We keep a list with references to _all_ entries, also the comments, + # this is the one we'll use to write out our new fstab, only 'real' + # entries (real == entries we understand) are added to self to let them + # be handled by iterator. + # allentries includes comments and invalid lines, self doesn't. + self.allentries.append(entry) + fhandle.close() + + if not sysfs_in_fstab: + sysfsentry = MountEntry(u"sysfs /sys sysfs defaults 0 0") + sysfsentry.notInFstab = True + sysfsentry.maydisable = False + #self.append(sysfsentry) + + if not usbdevfs_in_fstab: + usbdevfsentry = MountEntry(u"procbususb /proc/bus/usb usbdevfs defaults 0 0") + usbdevfsentry.notInFstab = True + usbdevfsentry.maydisable = False + self.append(usbdevfsentry) + + self.updateStatus() + + ######################################################################## + def append(self,entry): + self.entries.append(entry) + + ######################################################################## + def remove(self,entry): + self.allentries.remove(entry) + entry.cleanup() + + ######################################################################## + def updateStatus(self,entry=None): + mtablist = self.getMtabList() + if entry==None: + for entry in self.entries: + entry.updateStatus(mtablist) + else: + entry.updateStatus(mtablist) + + ######################################################################## + def getMtabList(self): + fhandle = open(self.mtab_filename) + mtablist = [] + for row in fhandle.readlines(): + if row.strip()[0]!='#': + parts = row.split() + mtablist.append(parts[1]) + fhandle.close() + return mtablist + + ######################################################################## + def updateFstabOnDisk(self): + fhandle = None + try: + try: + fhandle = codecs.open(self.fstab_filename+"~","w",locale.getpreferredencoding(),'replace') + for entry in self.allentries: + if not entry.notInFstab: + line = entry.getFstabLine() + fhandle.write(line+u"\n") + print line + fhandle.close() + fhandle = None + + # Move it over the original + os.rename(self.fstab_filename+"~",self.fstab_filename) + return True + finally: + if fhandle: + fhandle.close() + except IOError: + return False + + ######################################################################## + # We make this class look like a container, and just forward everything + # on to the entries attribute. + def __contains__(self,item): + return self.entries.__contains(item) + ######################################################################## + def __delitem__(self,key): + raise NotImplementedError, "No __delitem__ on MountTable." + + ######################################################################## + def __getitem__(self,key): + return self.entries.__getitem__(key) + ######################################################################## + + def __iter__(self): + return self.entries.__iter__() + ######################################################################## + def __len__(self): + return self.entries.__len__() + ######################################################################## + def __setitem__(self,key,value): + raise NotImplementedError, "No __setitem__ on MountTable." + +############################################################################ +class MountEntryDialogOptions(QWidget): + + deviceexample = i18n("(for example /dev/hdb3)") + + ######################################################################## + def __init__(self,parent,showmountpoint=True,showdevice=True, + showfs_freq=True,showfs_passno=True,showuuid=True,showlabel=True): + QWidget.__init__(self,parent) + self.showmountpoint = showmountpoint + self.showdevice = showdevice + self.showfs_freq = showfs_freq + self.showfs_passno = showfs_passno + self.showuuid = showuuid + self.showlabel = showlabel + self._fillPage() + # TODO: KDirSelectDialog needs "Create new Folder" + self.mountpointdialog = KDirSelectDialog("/",True,self,"Select Mount Point",True) + + ######################################################################## + def _fillPage(self): + row = 0 + grid = QGridLayout(self,1,2) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(2,0) + + if self.showmountpoint: + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,row,0) + + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + + if self.showdevice: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + + self.devicecheckbox = QRadioButton(i18n("by name"),hbox) + self.connect(self.devicecheckbox,SIGNAL("clicked()"), \ + self.slotDeviceCheckboxClicked) + self.devicelineedit = KLineEdit(hbox) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addMultiCellWidget(example,row,row,1,3) + row += 1 + + if self.showuuid: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.uuidcheckbox = QRadioButton(i18n("by UUID"),hbox) + self.connect(self.uuidcheckbox,SIGNAL("clicked()"), \ + self.slotUUIDCheckboxClicked) + self.uuidlineedit = KLineEdit(hbox) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + + if self.showlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.labelcheckbox = QRadioButton(i18n("by label"),hbox) + self.connect(self.labelcheckbox,SIGNAL("clicked()"), \ + self.slotLabelCheckboxClicked) + self.labellineedit = KLineEdit(hbox) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + else: + if self.showdevice: + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + self.devicelineedit = KLineEdit(self) + grid.addMultiCellWidget(self.devicelineedit,row,row,1,3) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + + if self.showuuid: + label = QLabel(i18n("Device UUID:"),self) + grid.addWidget(label,row,0) + self.uuidlineedit = KLineEdit(self) + grid.addMultiCellWidget(self.uuidlineedit,row,row,1,3) + row += 1 + + if self.showlabel: + label = QLabel(i18n("Device Label:"),self) + grid.addWidget(label,row,0) + self.labellineedit = KLineEdit(self) + grid.addMultiCellWidget(self.labellineedit,row,row,1,3) + row += 1 + + label = QLabel(i18n("Options:"),self) + grid.addWidget(label,row,0) + self.optionslineedit = KLineEdit(self) + grid.addMultiCellWidget(self.optionslineedit,row,row,1,3) + row += 1 + + if self.showfs_freq: + label = QLabel(i18n("fs_freq:"),self) + grid.addWidget(label,row,0) + self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,self) + grid.addWidget(self.fsfreqspinbox,row,1) + + if self.showfs_passno: + label = QLabel(i18n("fs_passno:"),self) + grid.addWidget(label,row,2) + self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,self) + grid.addWidget(self.fspassnospinbox,row,3) + row += 1 + + grid.addWidget(QWidget(self),row,0) + + for x in range(grid.numRows()): + grid.setRowStretch(x,0) + grid.setRowStretch(grid.numRows()-1,1) + + ######################################################################## + def displayMountEntry(self,entry): + global allowuuid, allowlabel + if self.showmountpoint: + self.mountpointlineedit.setText(entry.getMountPoint()) + + uuid = entry.getUUID() + if entry.getDevice() == "" and uuid != "": + device = microhal.getDeviceByUUID(uuid) + self.devicelineedit.setText(uuid) + else: + if self.showdevice: + self.devicelineedit.setText(entry.getDevice()) + + if self.showuuid: + if entry.getUUID()!="": + self.uuidlineedit.setText(entry.getUUID()) + + if self.showlabel: + if entry.getLabel()!="": + self.labellineedit.setText(entry.getLabel()) + + if entry.getUseAsDevice() == "devicenode": + if self.showdevice: + self.devicelineedit.setEnabled(True) + self.devicecheckbox.setChecked(True) + if self.showuuid: + self.uuidcheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + if self.showlabel: + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + elif entry.getUseAsDevice() == "label": + if self.showlabel: + self.labellineedit.setEnabled(True) + self.labelcheckbox.setChecked(True) + if self.showdevice: + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + if self.showuuid: + self.uuidlineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + elif entry.getUseAsDevice() == "uuid": + if self.showdevice: + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + if self.showlabel: + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + if self.showuuid: + self.uuidlineedit.setEnabled(True) + self.uuidcheckbox.setChecked(True) + +## if allowlabel: +## self.labellineedit.setText(entry.getLabel()) +## if allowuuid: +## self.uuidlineedit.setText(entry.getUUID()) +## self.devicelineedit.setText(entry.getDevice()) + + self.optionslineedit.setText(entry.getExtraOptions()) + if self.showfs_freq: + self.fsfreqspinbox.setValue(entry.getFSFreq()) + if self.showfs_passno: + self.fspassnospinbox.setValue(entry.getFSPassno()) + + ######################################################################## + def undisplayMountEntry(self,entry): + if self.showmountpoint: + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + if self.showdevice: + entry.setDevice( unicode(self.devicelineedit.text()) ) + + if self.showuuid and self.showdevice: + if self.devicecheckbox.isChecked(): + entry.setUUID(None) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + if self.showlabel and self.showdevice: + if self.devicecheckbox.isChecked(): + entry.setLabel(None) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + + if allowuuid and self.showuuid: + if self.uuidcheckbox.isChecked(): + entry.setUseAsDevice("uuid") + if allowlabel and self.showlabel: + if self.labelcheckbox.isChecked(): + entry.setUseAsDevice("label") + if self.showdevice: + if self.devicecheckbox.isChecked(): + entry.setUseAsDevice("devicenode") + + entry.setExtraOptions( unicode(self.optionslineedit.text()) ) + if self.showfs_freq: + entry.setFSFreq(self.fsfreqspinbox.value()) + if self.showfs_passno: + entry.setFSPassno(self.fspassnospinbox.value()) + + ######################################################################## + def slotBrowseMountPointClicked(self): + fileurl = KURL() + fileurl.setPath(self.mountpointlineedit.text()) + self.mountpointdialog.setCurrentURL(fileurl) + if self.mountpointdialog.exec_loop()==QDialog.Accepted: + self.mountpointlineedit.setText(self.mountpointdialog.url().path()) + + ######################################################################## + def slotDeviceCheckboxClicked(self): + self.uuidcheckbox.setChecked(False) + self.labelcheckbox.setChecked(False) + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + self.labellineedit.setEnabled(False) + + ######################################################################## + def slotUUIDCheckboxClicked(self): + if self.uuidlineedit.text() == "": + label = microhal.getUUIDByDevice(unicode(self.devicelineedit.text())) + self.uuidlineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + + def slotLabelCheckboxClicked(self): + if self.labellineedit.text() == "": + label = microhal.getLabelByDevice(unicode(self.devicelineedit.text())) + self.labellineedit.setText(label) + self.uuidcheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidlineedit.setEnabled(False) + self.labelcheckbox.setChecked(True) + self.labellineedit.setEnabled(True) + +############################################################################ +class MountEntryDialogOptionsCommonUnix(MountEntryDialogOptions): + + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent) + self.advanceddialog = MountEntryAdvancedCommonUnixDialog(None) + + ######################################################################## + def _fillPage(self): + + row = 0 + grid = QGridLayout(self,1,2) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setRowStretch(6,1) + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,row,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"), \ + self.slotBrowseMountPointClicked) + grid.addWidget(hbox,row,1) + row += 1 + + if self.showuuid or self.showlabel: + + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + + self.devicecheckbox = QRadioButton(i18n("by name"),hbox) + self.connect(self.devicecheckbox,SIGNAL("clicked()"), \ + self.slotDeviceCheckboxClicked) + self.devicelineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + if self.showuuid: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.uuidcheckbox = QRadioButton(i18n("by UUID"),hbox) + self.connect(self.uuidcheckbox,SIGNAL("clicked()"), \ + self.slotUUIDCheckboxClicked) + self.uuidlineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + if self.showlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.labelcheckbox = QRadioButton(i18n("by label"),hbox) + self.connect(self.labelcheckbox,SIGNAL("clicked()"), \ + self.slotLabelCheckboxClicked) + self.labellineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + else: + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + self.devicelineedit = KLineEdit(self) + grid.addWidget(self.devicelineedit,row,1) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + self.autocheckbox = QCheckBox(i18n("Enable at start up"),self) + grid.addWidget(self.autocheckbox,row,1) + row += 1 + + self.writeablecheckbox = QCheckBox(i18n("Writeable"),self) + grid.addWidget(self.writeablecheckbox,row,1) + row += 1 + + label = QLabel(i18n("Mount Permission:"),self) + grid.addWidget(label,row,0) + self.usermountcombobox = KComboBox(self) + self.usermountcombobox.insertItem(i18n("Root user only may enable/disable")) + self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable")) + self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime")) + self.usermountcombobox.insertItem(i18n("Device owner may enable/disable")) + grid.addWidget(self.usermountcombobox,row,1) + row += 1 + + #grid.addWidget(,9,0) + button = KPushButton(i18n("Advanced..."),self) + button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) + self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked) + grid.addWidget(button,row,1,Qt.AlignRight) + row += 1 + + grid.addWidget(QWidget(self),row,0) + + for x in range(grid.numRows()): + grid.setRowStretch(x,0) + grid.setRowStretch(grid.numRows()-1,1) + + ######################################################################## + def displayMountEntry(self,entry): + self.devicelineedit.setText(entry.getDevice()) + + if self.showuuid: + if entry.getUUID()!="": + self.uuidlineedit.setText(entry.getUUID()) + + if self.showlabel: + if entry.getLabel()!="": + self.labellineedit.setText(entry.getLabel()) + + if entry.getUseAsDevice() == "devicenode": + self.devicelineedit.setEnabled(True) + self.devicecheckbox.setChecked(True) + self.uuidcheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + elif entry.getUseAsDevice() == "label": + self.labellineedit.setEnabled(True) + self.labelcheckbox.setChecked(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + elif entry.getUseAsDevice() == "uuid": + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + self.uuidcheckbox.setChecked(True) + + self.devicelineedit.setText(entry.getDevice()) + self.mountpointlineedit.setText(entry.getMountPoint()) + self.options = entry.getExtraOptions() + self.fsfreq = entry.getFSFreq() + self.fspassno = entry.getFSPassno() + self.autocheckbox.setChecked(entry.getMountAtBoot()) + self.writeablecheckbox.setChecked(entry.getWritable()) + self.accesstime = entry.getAtime() + self.allowexecutable = entry.getAllowExecutables() + self.allowsuid = entry.getSUID() + self.usedevpoints = entry.getUseDevPoints() + self.usermountcombobox.setCurrentItem(entry.getAllowUserMount()) + + ######################################################################## + def undisplayMountEntry(self,entry): + + entry.setDevice( unicode(self.devicelineedit.text()) ) + if self.showuuid: + if self.devicecheckbox.isChecked() or self.labelcheckbox.isChecked(): + entry.setUUID(None) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + + if self.showlabel: + if self.devicecheckbox.isChecked() or self.uuidcheckbox.isChecked(): + entry.setLabel(None) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + + if not self.showlabel and not self.showuuid: + if self.uuidcheckbox.isChecked() or self.labelcheckbox.isChecked(): + entry.setLabel(None) + else: + entry.setLabel( unicode(self.devicelineedit.text()) ) + + if allowuuid: + if self.uuidcheckbox.isChecked(): + entry.setUseAsDevice("uuid") + if allowlabel: + if self.labelcheckbox.isChecked(): + entry.setUseAsDevice("label") + if self.devicecheckbox.isChecked(): + entry.setUseAsDevice("devicenode") + + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + entry.setExtraOptions(self.options) + entry.setFSFreq(self.fsfreq) + entry.setFSPassno(self.fspassno) + entry.setAtime(self.accesstime) + entry.setMountAtBoot(self.autocheckbox.isChecked()) + entry.setWritable(self.writeablecheckbox.isChecked()) + entry.setUseDevPoints(self.usedevpoints) + entry.setAllowExecutable(self.allowexecutable) + entry.setSUID(self.allowsuid) + entry.setAllowUserMount(self.usermountcombobox.currentItem()) + + ######################################################################## + def slotAdvancedClicked(self): + (self.accesstime, self.allowexecutable, self.allowsuid, self.usedevpoints, self.options, self.fsfreq, + self.fspassno)\ + = self.advanceddialog.do(self.accesstime, self.allowexecutable, self.allowsuid, self.usedevpoints, + self.options, self.fsfreq, self.fspassno) + + ######################################################################## + def slotDeviceCheckboxClicked(self): + self.uuidcheckbox.setChecked(False) + self.devicelineedit.setEnabled(True) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + self.uuidlineedit.setEnabled(False) + + ######################################################################## + def slotUUIDCheckboxClicked(self): + if self.uuidlineedit.text() == "": + label = microhal.getUUIDByDevice(unicode(self.devicelineedit.text())) + self.uuidlineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.labelcheckbox.setChecked(False) + self.labellineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + + def slotLabelCheckboxClicked(self): + if self.labellineedit.text() == "": + label = microhal.getLabelByDevice(unicode(self.devicelineedit.text())) + self.labellineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + self.uuidlineedit.setEnabled(False) + self.labellineedit.setEnabled(True) + +############################################################################ +class MountEntryDialogOptionsSys(MountEntryDialogOptions): + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent,True,False,False,False,False,False) + +############################################################################ +class MountEntryDialogOptionsSwap(MountEntryDialogOptions): + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent,False,True,False,False) + +class MountEntryDialogOptionsNfs(MountEntryDialogOptionsCommonUnix): + + deviceexample = i18n("(for example 192.168.1.66:/export)") + +############################################################################ +class MountEntryDialogOptionsVFAT(MountEntryDialogOptions): + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent) + self.advanceddialog = MountEntryAdvancedPlainDialog(None) + self.updatinggui= False + + ######################################################################## + def _fillPage(self): + global allowuuid, allowlabel + + row = 0 + grid = QGridLayout(self,11,2) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(2,0) + grid.setRowStretch(10,1) + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,row,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked) + grid.addMultiCellWidget(hbox,row,row,1,3) + row += 1 + + if allowuuid or allowlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + + self.devicecheckbox = QRadioButton(i18n("by name"),hbox) + self.connect(self.devicecheckbox,SIGNAL("clicked()"), \ + self.slotDeviceCheckboxClicked) + self.devicelineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + if allowuuid and self.showuuid: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.uuidcheckbox = QRadioButton(i18n("by UUID"),hbox) + self.connect(self.uuidcheckbox,SIGNAL("clicked()"), \ + self.slotUUIDCheckboxClicked) + self.uuidlineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + if allowlabel and self.showlabel: + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.labelcheckbox = QRadioButton(i18n("by label"),hbox) + self.connect(self.labelcheckbox,SIGNAL("clicked()"), \ + self.slotLabelCheckboxClicked) + self.labellineedit = KLineEdit(hbox) + grid.addWidget(hbox,row,1) + row += 1 + + else: + + label = QLabel(i18n("Device:"),self) + grid.addWidget(label,row,0) + self.devicelineedit = KLineEdit(self) + grid.addMultiCellWidget(self.devicelineedit,row,row,1,3) + row += 1 + + example = QLabel(self.deviceexample,self) + grid.addWidget(example,row,1) + row += 1 + + self.autocheckbox = QCheckBox(i18n("Enable at start up"),self) + grid.addMultiCellWidget(self.autocheckbox,row,row,1,3) + row += 1 + + # Security & Safety line. + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(hbox) + tmplabel.setPixmap(UserIcon("hi16-lock")) + hbox.setStretchFactor(tmplabel,0) + tmplabel = QLabel(hbox) + tmplabel.setText(i18n("Security & Safety")) + hbox.setStretchFactor(tmplabel,0) + sep = KSeparator(KSeparator.Horizontal,hbox) + hbox.setStretchFactor(sep,1) + grid.addMultiCellWidget(hbox,row,row,0,3) + row += 1 + + self.writeablecheckbox = QCheckBox(i18n("Writeable"),self) + grid.addMultiCellWidget(self.writeablecheckbox,row,row,1,3) + row += 1 + + label = QLabel(i18n("Files belong to user:"),self) + grid.addWidget(label,row,0) + self.uidcombobox = UserComboBox(self) + grid.addMultiCellWidget(self.uidcombobox,row,row,1,3) + row += 1 + + label = QLabel(i18n("Files belong to group:"),self) + grid.addWidget(label,row,0) + self.gidcombobox = GroupComboBox(self) + grid.addMultiCellWidget(self.gidcombobox,row,row,1,3) + row += 1 + + label = QLabel(i18n("Mount Permission:"),self) + grid.addWidget(label,row,0) + self.usermountcombobox = KComboBox(self) + self.usermountcombobox.insertItem(i18n("Root user only may enable/disable")) + self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable")) + self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime")) + self.usermountcombobox.insertItem(i18n("Device owner may enable/disable")) + grid.addMultiCellWidget(self.usermountcombobox,row,row,1,3) + row += 1 + + self.suppresspermissionerrorcheckbox = QCheckBox(i18n("Suppress permission errors"),self) + grid.addMultiCellWidget(self.suppresspermissionerrorcheckbox,row,row,1,3) + row += 1 + + row += 1 + button = KPushButton(i18n("Advanced..."),self) + button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) + self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked) + grid.addMultiCellWidget(button,row,row,1,3,Qt.AlignRight) + + ######################################################################## + def displayMountEntry(self,entry): + global allowuuid, allowlabel + + uuid = entry.getUUID() + if entry.getDevice() == "" and uuid != "": + device = microhal.getDeviceByUUID(uuid) + self.devicelineedit.setText(uuid) + else: + self.devicelineedit.setText(entry.getDevice()) + + if allowuuid: + self.uuidlineedit.setText(uuid) + if entry.getUUID()!="": + self.uuidlineedit.setEnabled(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.uuidcheckbox.setChecked(True) + else: + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + self.devicecheckbox.setChecked(True) + self.uuidcheckbox.setChecked(False) + + if allowlabel: # If the filesystem has a label override the UUID settings + self.labellineedit.setText(entry.getLabel()) + if entry.getLabel()!="": + self.labellineedit.setEnabled(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.labelcheckbox.setChecked(True) + self.uuidlineedit.setEnabled(False) + self.uuidcheckbox.setChecked(False) + else: + if entry.getUUID()!="": + self.uuidlineedit.setEnabled(True) + self.devicelineedit.setEnabled(False) + self.devicecheckbox.setChecked(False) + self.uuidcheckbox.setChecked(True) + else: + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + self.devicecheckbox.setChecked(True) + self.uuidcheckbox.setChecked(False) + + if allowlabel: + self.labellineedit.setText(entry.getLabel()) + if allowuuid: + self.uuidlineedit.setText(entry.getUUID()) + self.devicelineedit.setText(entry.getDevice()) + + self.mountpointlineedit.setText(entry.getMountPoint()) + self.options = entry.getExtraOptions() + self.fsfreq = entry.getFSFreq() + self.fspassno = entry.getFSPassno() + self.writeablecheckbox.setChecked(entry.getWritable()) + self.autocheckbox.setChecked(entry.getMountAtBoot()) + self.usermountcombobox.setCurrentItem(entry.getAllowUserMount()) + self.uidcombobox.setUID(entry.getUID()) + self.gidcombobox.setGID(entry.getGID()) + self.suppresspermissionerrorcheckbox.setChecked(entry.getSuppressPermissionErrors()) + + ######################################################################## + def undisplayMountEntry(self,entry): + global allowuuid, allowlabel + if allowuuid: + if self.devicecheckbox.isChecked(): + entry.setUUID(None) + else: + if allowlabel: + if self.labelcheckbox.isChecked(): + entry.setLabel( unicode(self.labellineedit.text()) ) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + else: + entry.setUUID( unicode(self.uuidlineedit.text()) ) + + if allowlabel: + if self.devicecheckbox.isChecked(): + entry.setLabel(None) + else: + if allowuuid: + if self.uuidcheckbox.isChecked(): + entry.setUUID( unicode(self.uuidlineedit.text()) ) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + else: + entry.setLabel( unicode(self.labellineedit.text()) ) + + entry.setDevice( unicode(self.devicelineedit.text()) ) + + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + entry.setExtraOptions(self.options) + entry.setFSFreq(self.fsfreq) + entry.setFSPassno(self.fspassno) + entry.setMountAtBoot(self.autocheckbox.isChecked()) + entry.setWritable(self.writeablecheckbox.isChecked()) + entry.setAllowUserMount(self.usermountcombobox.currentItem()) + entry.setUID(self.uidcombobox.UID()) + entry.setGID(self.gidcombobox.GID()) + entry.setSuppressPermissionErrors(self.suppresspermissionerrorcheckbox.isChecked()) + + ######################################################################## + def slotAdvancedClicked(self): + (self.options, self.fsfreq, self.fspassno)\ + = self.advanceddialog.do(self.options, self.fsfreq, self.fspassno) + + ######################################################################## + def slotDeviceCheckboxClicked(self): + self.uuidcheckbox.setChecked(False) + self.devicelineedit.setEnabled(True) + self.uuidlineedit.setEnabled(False) + + ######################################################################## + def slotUUIDCheckboxClicked(self): + if self.uuidlineedit.text() == "": + label = microhal.getUUIDByDevice(unicode(self.devicelineedit.text())) + self.uuidlineedit.setText(label) + self.devicecheckbox.setChecked(False) + self.devicelineedit.setEnabled(False) + self.uuidlineedit.setEnabled(True) + +############################################################################ +class MountEntryDialogOptionsSMB(MountEntryDialogOptions): + + ######################################################################## + def __init__(self,parent): + MountEntryDialogOptions.__init__(self,parent) + self.advanceddialog = MountEntryAdvancedPlainDialog(None) + self.updatinggui= False + + ######################################################################## + def _fillPage(self): + grid = QGridLayout(self,14,4) + grid.setSpacing(KDialog.spacingHint()) + grid.setColStretch(0,0) + grid.setColStretch(2,0) + grid.setRowStretch(12,1) + + label = QLabel(i18n("Mount Point:"),self) + grid.addWidget(label,0,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.mountpointlineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.mountpointlineedit,1) + #self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged) + self.mountpointbutton = KPushButton(i18n("Browse..."),hbox) + hbox.setStretchFactor(self.mountpointbutton,0) + self.connect(self.mountpointbutton,SIGNAL("clicked()"),self.slotBrowseMountPointClicked) + grid.addMultiCellWidget(hbox,0,0,1,3) + + label = QLabel(i18n("Network Share:"),self) + grid.addWidget(label,1,0) + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + self.devicelineedit = KLineEdit(hbox) + hbox.setStretchFactor(self.devicelineedit,1) + self.devicelinebutton = KPushButton(i18n("Scan..."),hbox) + hbox.setStretchFactor(self.devicelinebutton,0) + self.connect(self.devicelinebutton,SIGNAL("clicked()"),self.slotBrowseDeviceLineClicked) + grid.addMultiCellWidget(hbox,1,1,1,3) + + # Connect as: + connectaslabel = QLabel(self) + connectaslabel.setText(i18n("Connect as:")) + grid.addWidget(connectaslabel,2,0) + + self.guestradio = QRadioButton(self) + self.guestradio.setChecked(True) + grid.addWidget(self.guestradio,2,1) + tmplabel = QLabel(self) + tmplabel.setText(i18n("Guest")) + grid.addMultiCellWidget(tmplabel,2,2,2,3) + self.connect(self.guestradio,SIGNAL("stateChanged(int)"),self.slotGuestRadioClicked) + + self.userradio = QRadioButton(self) + grid.addWidget(self.userradio,3,1) + tmplabel = QLabel(self) + tmplabel.setText(i18n("Username:")) + grid.addWidget(tmplabel,3,2) + self.connect(self.userradio,SIGNAL("stateChanged(int)"),self.slotUserRadioClicked) + + self.usernameedit = KLineEdit(self) + grid.addWidget(self.usernameedit,3,3) + + tmplabel = QLabel(self) + tmplabel.setText(i18n("Password:")) + grid.addWidget(tmplabel,4,2) + + self.passwordedit = KLineEdit(self) + grid.addWidget(self.passwordedit,4,3) + + self.autocheckbox = QCheckBox(i18n("Enable at start up"),self) + grid.addMultiCellWidget(self.autocheckbox,5,5,1,3) + + # Security & Safety line. + hbox = QHBox(self) + hbox.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(hbox) + tmplabel.setPixmap(UserIcon("hi16-lock")) + hbox.setStretchFactor(tmplabel,0) + tmplabel = QLabel(hbox) + tmplabel.setText(i18n("Security & Safety")) + hbox.setStretchFactor(tmplabel,0) + sep = KSeparator(KSeparator.Horizontal,hbox) + hbox.setStretchFactor(sep,1) + grid.addMultiCellWidget(hbox,6,6,0,3) + + self.writeablecheckbox = QCheckBox(i18n("Writeable"),self) + grid.addMultiCellWidget(self.writeablecheckbox,7,7,1,3) + + label = QLabel(i18n("Files belong to user:"),self) + grid.addWidget(label,8,0) + self.uidcombobox = UserComboBox(self) + grid.addMultiCellWidget(self.uidcombobox,8,8,1,3) + + label = QLabel(i18n("Files belong to group:"),self) + grid.addWidget(label,9,0) + self.gidcombobox = GroupComboBox(self) + grid.addMultiCellWidget(self.gidcombobox,9,9,1,3) + + label = QLabel(i18n("Mount Permission:"),self) + grid.addWidget(label,10,0) + self.usermountcombobox = KComboBox(self) + self.usermountcombobox.insertItem(i18n("Root user only may enable/disable")) + self.usermountcombobox.insertItem(i18n("One user at a time may enable/disable")) + self.usermountcombobox.insertItem(i18n("Any user may enable/disable anytime")) + self.usermountcombobox.insertItem(i18n("Device owner may enable/disable")) + grid.addMultiCellWidget(self.usermountcombobox,10,10,1,3) + + button = KPushButton(i18n("Advanced..."),self) + button.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed) + self.connect(button,SIGNAL("clicked()"),self.slotAdvancedClicked) + grid.addMultiCellWidget(button,13,13,1,3,Qt.AlignRight) + + self.selectsmbdialog = None + + ######################################################################## + def slotBrowseDeviceLineClicked(self): + if self.updatinggui: + return + self.updatinggui = True + + if self.selectsmbdialog is None: + self.selectsmbdialog = SMBShareSelectDialog(None) + + # This just converts a \\zootv\data\ share name to smb:/zootv/data + parts = [x.replace("/",'\\') for x in unicode(self.devicelineedit.text()).split('\\') if x!=""] + oldurl = u"smb:/"+("/".join(parts) ) + + urlobj = KURL(oldurl) + if self.userradio.isChecked(): + urlobj.setUser(self.usernameedit.text()) + urlobj.setPass(self.passwordedit.text()) + + newurlobj = self.selectsmbdialog.choose(urlobj) + # This just converts smb:/zootv/data to \\zootv\data. + plainurl = KURL(newurlobj) + plainurl.setUser(QString.null) + plainurl.setPass(QString.null) + parts = [x.replace('\\',"/") for x in unicode(plainurl.url())[4:].split("/") if x !=""] + #convert the first part to an IP address + nmboutput = subprocess.Popen(["nmblookup",parts[0]], stdout=subprocess.PIPE).stdout + nmboutput.readline() + ipaddress = nmboutput.readline().split(" ")[0] + parts[0] = ipaddress + self.devicelineedit.setText(r'\\'+('\\'.join(parts))) + + if not newurlobj.hasUser(): + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + self.usernameedit.setText("") + self.passwordedit.setText("") + else: + self.guestradio.setChecked(False) + self.userradio.setChecked(True) + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + self.usernameedit.setText(newurlobj.user()) + self.passwordedit.setText(newurlobj.pass_()) + + self.updatinggui = False + + ######################################################################## + def displayMountEntry(self,entry): + self.devicelineedit.setText(entry.getDevice()) + self.mountpointlineedit.setText(entry.getMountPoint()) + self.options = entry.getExtraOptions() + self.fsfreq = entry.getFSFreq() + self.fspassno = entry.getFSPassno() + self.writeablecheckbox.setChecked(entry.getWritable()) + self.autocheckbox.setChecked(entry.getMountAtBoot()) + self.usermountcombobox.setCurrentItem(entry.getAllowUserMount()) + self.uidcombobox.setUID(entry.getUID()) + self.gidcombobox.setGID(entry.getGID()) + + if entry.getUsername() is None: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + self.usernameedit.setText("") + self.passwordedit.setText("") + else: + self.guestradio.setChecked(False) + self.userradio.setChecked(True) + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + self.usernameedit.setText(entry.getUsername()) + self.passwordedit.setText(entry.getPassword()) + + ######################################################################## + def undisplayMountEntry(self,entry): + entry.setDevice( unicode(self.devicelineedit.text()) ) + entry.setMountPoint( unicode(self.mountpointlineedit.text()) ) + entry.setExtraOptions(self.options) + entry.setFSFreq(self.fsfreq) + entry.setFSPassno(self.fspassno) + entry.setMountAtBoot(self.autocheckbox.isChecked()) + entry.setWritable(self.writeablecheckbox.isChecked()) + entry.setAllowUserMount(self.usermountcombobox.currentItem()) + entry.setUID(self.uidcombobox.UID()) + entry.setGID(self.gidcombobox.GID()) + + if self.guestradio.isChecked(): + entry.setUsername(None) + entry.setPassword(None) + else: + entry.setUsername( unicode(self.usernameedit.text()) ) + entry.setPassword( unicode(self.passwordedit.text()) ) + + ######################################################################## + def slotAdvancedClicked(self): + (self.options, self.fsfreq, self.fspassno)\ + = self.advanceddialog.do(self.options, self.fsfreq, self.fspassno) + + ######################################################################## + def slotGuestRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + + if state==QButton.Off: + self.guestradio.setChecked(True) + self.userradio.setChecked(False) + + self.passwordedit.setEnabled(False) + self.usernameedit.setEnabled(False) + + self.updatinggui = False + + ######################################################################## + def slotUserRadioClicked(self,state): + if self.updatinggui: + return + self.updatinggui = True + if state==QButton.Off: + self.userradio.setChecked(True) + self.guestradio.setChecked(False) + + self.passwordedit.setEnabled(True) + self.usernameedit.setEnabled(True) + + self.updatinggui = False + +############################################################################ +class ROListBoxItem(QListBoxPixmap): + """A read-only ListBox item that also uses the 'alternate' background colour + as background. + """ + def __init__(self,listbox,pix,text): + QListBoxPixmap.__init__(self,listbox,pix,text) + self.setSelectable(False) + def paint(self,p): + boldfont = QFont(p.font()) + boldfont.setWeight(QFont.Bold) + p.setFont(boldfont) + p.setBackgroundColor(KGlobalSettings.alternateBackgroundColor()) + p.eraseRect(0,0,self.listBox().width(),self.height(self.listBox())) + QListBoxPixmap.paint(self,p) + +############################################################################ +class MountEntryDialog(KDialogBase): + + MountTypeEditorsDisk = { + 'ext2' : MountEntryDialogOptionsCommonUnix, + 'ext3' : MountEntryDialogOptionsCommonUnix, + 'reiserfs' : MountEntryDialogOptionsCommonUnix, + 'reiser4' : MountEntryDialogOptionsCommonUnix, + 'xfs' : MountEntryDialogOptionsCommonUnix, + 'jfs' : MountEntryDialogOptionsCommonUnix, + 'vfat' : MountEntryDialogOptionsVFAT, + 'ntfs' : MountEntryDialogOptionsVFAT, + 'hfsplus' : MountEntryDialogOptionsVFAT, + 'udf' : MountEntryDialogOptions, + 'iso9660' : MountEntryDialogOptions, + } + MountTypeEditorsNetwork = { + 'nfs' : MountEntryDialogOptionsNfs, + 'cifs' : MountEntryDialogOptionsSMB, + } + MountTypeEditorsSystem = { + 'proc' : MountEntryDialogOptionsSys, + 'sysfs' : MountEntryDialogOptionsSys, + 'rootfs' : MountEntryDialogOptions, + 'bdev' : MountEntryDialogOptions, + 'sockfs' : MountEntryDialogOptions, + 'tmpfs' : MountEntryDialogOptions, + 'shm' : MountEntryDialogOptions, + 'pipefs' : MountEntryDialogOptions, + 'ramfs' : MountEntryDialogOptionsSys, + 'devfs' : MountEntryDialogOptions, + 'devpts' : MountEntryDialogOptionsSys, + 'auto' : MountEntryDialogOptionsCommonUnix, + 'usbdevfs' : MountEntryDialogOptions, + 'procbususb' : MountEntryDialogOptions, + 'usbfs' : MountEntryDialogOptions, + 'supermount' : MountEntryDialogOptions, + 'swap' : MountEntryDialogOptionsSwap + } + + ######################################################################## + def __init__(self,parent): + KDialogBase.__init__(self,parent,None,True,"Configuration",KDialogBase.Ok|KDialogBase.Cancel, + KDialogBase.Cancel) + + self.updatingGUI = True + + # Maps MountEntry classes to MountEntryDialogOptions objects + self.mountTypeToOptionWidget = {} + + # Maps indexes in the combobox to mounttypes + self.comboIndexToMountType = [] + self.currentOptionWidget = None + + self.topvbox = QVBox(self) + self.setMainWidget(self.topvbox) + self.topvbox.setSpacing(self.spacingHint()) + + hb = QHBox(self.topvbox) + hb.setSpacing(self.spacingHint()) + self.topvbox.setStretchFactor(hb,0) + + label = QLabel(i18n("Type:"),hb) + hb.setStretchFactor(label,0) + self.mounttypecombo = KComboBox(hb) + + # Disk types + ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-hdd"),i18n("Disk Filesystems")) + self.comboIndexToMountType.append(None) + items = self.MountTypeEditorsDisk.keys() + items.sort() + for mounttype in items: + self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype)) + self.comboIndexToMountType.append(mounttype) + + # Network types + ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-network"),i18n("Network Filesystems")) + self.comboIndexToMountType.append(None) + items = self.MountTypeEditorsNetwork.keys() + items.sort() + for mounttype in items: + self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype)) + self.comboIndexToMountType.append(mounttype) + + # System types + ROListBoxItem(self.mounttypecombo.listBox(),UserIcon("hi16-blockdevice"),i18n("Operating System")) + self.comboIndexToMountType.append(None) + items = self.MountTypeEditorsSystem.keys() + items.sort() + for mounttype in items: + self.mounttypecombo.insertItem(MountEntry.getMountTypeLongName(mounttype)) + self.comboIndexToMountType.append(mounttype) + + self.MountTypeEditors = self.MountTypeEditorsDisk.copy() + self.MountTypeEditors.update(self.MountTypeEditorsNetwork) + self.MountTypeEditors.update(self.MountTypeEditorsSystem) + + #hb.setStretchFactor(self.runlevelcombo,0) + self.connect(self.mounttypecombo, SIGNAL("activated(int)"), self.slotMountTypeChanged) + + widget = QWidget(hb) + hb.setStretchFactor(widget,1) + + # Create the stack of option edit widgets. + gb = QVGroupBox(self.topvbox) + self.topvbox.setStretchFactor(gb,1) + self.optionsstack = QWidgetStack(gb) + + for mounttype in self.MountTypeEditors: + editpage = self.MountTypeEditors[mounttype](self.optionsstack) + self.mountTypeToOptionWidget[mounttype] = editpage + self.optionsstack.addWidget(editpage) + + self.fsunavailablelabel = QHBox(gb) + self.fsunavailablelabel.setSpacing(KDialog.spacingHint()) + tmplabel = QLabel(self.fsunavailablelabel) + self.fsunavailablelabel.setStretchFactor(tmplabel,0) + tmplabel.setPixmap(SmallIcon('info')) + label = QLabel(i18n("This filesystem type is currently unavailable on the running kernel."), + self.fsunavailablelabel) + self.fsunavailablelabel.setStretchFactor(label,1) + self.fsunavailablelabel.hide() + + self.updatingGUI = False + + ####################################################################### + def doEditMount(self,mounttable,mountentry): + self.newEntry = False + self.mounttable = mounttable + self.originalMountEntry = mountentry + self.currentMountEntry = mountentry.copy() + + self.updatingGUI = True + self.selectEntry(self.currentMountEntry.getMountType()) + self.updatingGUI = False + if self.exec_loop()==QDialog.Accepted: + # All of the update stuff is in slotOk() + return True + return False + + ####################################################################### + def doNewMount(self,mounttable,defaultdevice): + self.newEntry = True + self.mounttable = mounttable + self.currentMountEntry = MountEntry() + if defaultdevice is not None: + self.currentMountEntry.setDevice(defaultdevice) + self.updatingGUI = True + self.currentOptionWidget = None + self.selectEntry(self.currentMountEntry.mounttype) + self.updatingGUI = False + if self.exec_loop()==QDialog.Accepted: + self.mounttable.allentries.append(self.currentMountEntry) + self.mounttable.updateFstabOnDisk() + return self.currentMountEntry + return None + + ####################################################################### + def selectEntry(self,mounttype): + #if self.currentOptionWidget!=None: + # # Update the mount entry from the + # self.currentOptionWidget.undisplayMountEntry(self.currentMountEntry) + self.currentMountEntry.setMountType(mounttype) + # Update GUI + self.mounttypecombo.setCurrentItem(self.comboIndexToMountType.index(mounttype)) + self.currentOptionWidget = self.mountTypeToOptionWidget[mounttype] + self.currentOptionWidget.displayMountEntry(self.currentMountEntry) + self.optionsstack.raiseWidget(self.currentOptionWidget) + if microhal.isSupportedFileSystem(mounttype): + self.fsunavailablelabel.hide() + else: + self.fsunavailablelabel.show() + + ####################################################################### + def slotMountTypeChanged(self,index): + if self.updatingGUI==False: + self.updatingGUI = True + self.selectEntry(self.comboIndexToMountType[index]) + self.updatingGUI = False + + ####################################################################### + def slotOk(self): + global allowlabel, allowuuid + self.currentOptionWidget.undisplayMountEntry(self.currentMountEntry) + if allowuuid: + if self.currentOptionWidget.uuidcheckbox.isChecked(): + self.currentMountEntry.setUseAsDevice("uuid") + if allowlabel: + if self.currentOptionWidget.labelcheckbox.isChecked(): + self.currentMountEntry.setUseAsDevice("label") + conflictentry = None + if self.newEntry: + for entry in self.mounttable: + if entry.getMountPoint()==self.currentMountEntry.getMountPoint(): + # Conflict found. + conflictentry = entry + else: + # Check if the mountpoint is already in use elsewhere in the mounttable. + if self.originalMountEntry.getMountPoint()!=self.currentMountEntry.getMountPoint(): + for entry in self.mounttable: + if (entry.getMountPoint()==self.currentMountEntry.getMountPoint() + and entry is not self.originalMountEntry): + # Conflict found. + conflictentry = entry + if conflictentry is not None: + if KMessageBox.warningContinueCancel(self, \ + i18n("The mountpoint '%1' is already in use by another entry?\nContinue?").arg( + self.currentMountEntry.getMountPoint()), \ + i18n("Mountpoint already in use"))!=KMessageBox.Continue: + return + + if self.currentMountEntry.getMountType() in MountEntryDialog.MountTypeEditorsDisk.keys(): + # If device is not in /dev and it's no bind mount, ask if that's meant this way ... + options = self.currentMountEntry.getFstabOptions() + if (not self.currentMountEntry.getDevice().startswith("/dev/") + and not ("loop" in options or "bind" in options)): + ask = KMessageBox.warningYesNoCancel(self, + i18n("'%1' does not seem to be a device and the option 'bind' has not been specified in the \ + \"Advanced\" page?\n Should I add the 'loop' option?").arg(self.currentMountEntry.device), + i18n("Options may be missing")) + if ask==KMessageBox.Cancel: + return + elif ask==KMessageBox.Yes: + # Add loop option + extraoptions = self.currentMountEntry.getExtraOptions().split(',') + extraoptions.append('loop') + self.currentMountEntry.setExtraOptions(','.join(extraoptions)) + + if (not os.path.isdir(self.currentMountEntry.getMountPoint()) + and not os.path.isfile(self.currentMountEntry.getMountPoint()) + and not self.currentMountEntry.mounttype == 'swap'): + ask = KMessageBox.warningYesNoCancel(self, + i18n("""The mountpoint '%1' does not exist. You will not be able to enable it until it is created.\ + \nShould I create the mountpoint?""").arg(self.currentMountEntry.getMountPoint()), + i18n("Mountpoint does not exist")) + if ask==KMessageBox.Cancel: + return + elif ask==KMessageBox.Yes: + os.mkdir(self.currentMountEntry.getMountPoint()) + elif os.path.isfile(self.currentMountEntry.getMountPoint()): + if KMessageBox.warningContinueCancel(self, + i18n("""The mountpoint '%1' is a file, but it has to be a directory. You will probably not \ + be able to enable it.\nContinue?""").arg(self.currentMountEntry.getMountPoint()), + i18n("Invalid mountpoint"))!=KMessageBox.Continue: + return + + if self.newEntry==False: + # How to Change a Mount Entry. + # 1. Disable the exisiting entry (if needed) + # 2. Update existing entry from the mount table. + # 3. Enable the updated entry (if needed) + # 4. Write new fstab file. + # 5. Enable the new entry (if needed) + + # 1. Disable the exisiting entry (if needed) + enabled = self.originalMountEntry.isEnabled() + if enabled: + self.disablingold = True + self.originalMountEntry.disable(self) + self.originalMountEntry.inPlaceCopyFrom(self.currentMountEntry) + self.mounttable.updateFstabOnDisk() + if enabled and self.originalMountEntry.isFileSystemAvailable(): + self.originalMountEntry.enable(self) + self.accept() + +############################################################################ +class MountEntryAdvancedCommonUnixDialog(KDialogBase): + ######################################################################## + def __init__(self,parent,name=None): + KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel) + + grid = self.makeGridMainWidget(2,Qt.Horizontal) + grid.setSpacing(self.spacingHint()) + + QWidget(grid) + self.accesstimecheckbox = QCheckBox(i18n("Update file access timestamps"),grid) + + QWidget(grid) + self.allowexecutablecheckbox = QCheckBox(i18n("Allow Executables"),grid) + + QWidget(grid) + self.allowsuidcheckbox = QCheckBox(i18n("Allow the SUID and SGID attributes"),grid) + + QWidget(grid) + self.usedevpointscheckbox = QCheckBox(i18n("Allow device points"),grid) + + label = QLabel(i18n("Options:"),grid) + self.optionslineedit = KLineEdit(grid) + + label = QLabel(i18n("fs_freq:"),grid) + self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,grid) + + label = QLabel(i18n("fs_passno:"),grid) + self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,grid) + + ######################################################################## + def do(self,atime,allowexecutable,allowsuid,usedevpoints,options,fsfreq,fspassno): + + self.accesstimecheckbox.setChecked(atime) + self.allowexecutablecheckbox.setChecked(allowexecutable) + self.allowsuidcheckbox.setChecked(allowsuid) + self.usedevpointscheckbox.setChecked(usedevpoints) + self.optionslineedit.setText(options) + self.fsfreqspinbox.setValue(fsfreq) + self.fspassnospinbox.setValue(fspassno) + self.exec_loop() + return ( self.accesstimecheckbox.isChecked(), + self.allowexecutablecheckbox.isChecked(), + self.allowsuidcheckbox.isChecked(), + self.usedevpointscheckbox.isChecked(), + unicode(self.optionslineedit.text()), + self.fsfreqspinbox.value(), + self.fspassnospinbox.value()) + +############################################################################ +class MountEntryAdvancedPlainDialog(KDialogBase): + ######################################################################## + def __init__(self,parent,name=None): + KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok|KDialogBase.Cancel) + + grid = self.makeGridMainWidget(2,Qt.Horizontal) + grid.setSpacing(self.spacingHint()) + + label = QLabel(i18n("Options:"),grid) + self.optionslineedit = KLineEdit(grid) + + label = QLabel(i18n("fs_freq:"),grid) + self.fsfreqspinbox = KIntSpinBox (0,1000,1,0,10,grid) + + label = QLabel(i18n("fs_passno:"),grid) + self.fspassnospinbox = KIntSpinBox (0,1000,1,0,10,grid) + + ######################################################################## + def do(self,options,fsfreq,fspassno): + self.optionslineedit.setText(options) + self.fsfreqspinbox.setValue(fsfreq) + self.fspassnospinbox.setValue(fspassno) + self.exec_loop() + return (unicode(self.optionslineedit.text()), self.fsfreqspinbox.value(), self.fspassnospinbox.value()) + +############################################################################ +class MountListViewItem(KListViewItem): + ######################################################################## + def __init__(self,parentitem,mountentry,haldevice=None): + self.haldevice = haldevice + self.mountentry = mountentry + if self.mountentry is None: + # There is no mount entry right now. This acts as a place holder + # for now. + KListViewItem.__init__(self,parentitem,self.haldevice.getName(),"","",self.haldevice.getDev(),"") + else: + if mountentry.isEnabled(): + enabled = i18n("Enabled") + else: + enabled = i18n("Disabled") + if self.haldevice is not None: + name = self.haldevice.getName() + else: + name = self.mountentry.getName() + KListViewItem.__init__(self, parentitem, \ + name, + self.mountentry.getMountPoint(), \ + self.mountentry.mounttype, \ + self.mountentry.getDevice(), \ + enabled) + + if self.mountentry.isEnabled(): + self.setPixmap(4,UserIcon("greenled")) + else: + self.setPixmap(4,UserIcon("greyled")) + self.__updateIcon() + + ######################################################################## + def hasHAL(self): + return self.haldevice is not None + + ######################################################################## + def getHAL(self): return self.haldevice + + ######################################################################## + def updateDisplay(self): + if self.mountentry is not None: + if self.mountentry.isEnabled(): + enabled = i18n("Enabled") + self.setPixmap(4,UserIcon("greenled")) + else: + enabled = i18n("Disabled") + self.setPixmap(4,UserIcon("greyled")) + + if self.haldevice is not None: + self.setText(0,self.haldevice.getName()) + else: + self.setText(0,self.mountentry.getMountPoint()) + + self.setText(1,self.mountentry.getMountPoint()) + self.setText(2,self.mountentry.mounttype) + + if self.mountentry.getDevice() is not None: + self.setText(3,self.mountentry.getDevice()) + else: + uuid_device = microhal.getDeviceByUUID(self.mountentry.getUUID()) + label_device = microhal.getDeviceByUUID(self.mountentry.getUUID()) + if label_device is not None: + self.setText(3,label_device.getDev()+" (Label)") + elif real_device is not None: + self.setText(3,real_device.getDev()+" (UUID)") + else: + self.setText(3,"UUID="+self.mountentry.getUUID()) + + self.setText(4,enabled) + else: + self.setText(0,self.haldevice.getName()) + self.setText(1,"") + self.setText(2,"") + self.setText(3,self.haldevice.getDev()) + self.setText(4,"") + self.setPixmap(4,QPixmap()) + self.__updateIcon() + + ######################################################################## + def setMountEntry(self,entry): + self.mountentry = entry + self.updateDisplay() + + ######################################################################## + def getMountEntry(self): + return self.mountentry + + ######################################################################## + def getDevice(self): return self.haldevice.getDev() + + ######################################################################## + + def __updateIcon(self): + if self.haldevice is not None: + self.setPixmap(0,UserIcon(self.haldevice.iconname)) + else: + self.setPixmap(0,UserIcon(self.mountentry.getIconName())) + +############################################################################ +class MountGroupListViewItem(KListViewItem): + ######################################################################## + def __init__(self,parentitem,haldevice): + self.haldevice = haldevice + KListViewItem.__init__(self,parentitem,self.haldevice.getName(),"","","","") + + if self.haldevice is not None: + iconname = self.haldevice.iconname + else: + iconname = self.mountentry.getIconName() + self.setPixmap(0,UserIcon(iconname)) + + ######################################################################## + def getMountEntry(self): + return None + + ######################################################################## + def updateDisplay(self): + pass + + def hasHAL(self): + return False + +############################################################################ +# Try translating this code to C++. I dare ya! +if standalone: + programbase = KDialogBase +else: + programbase = KCModule + +class MountConfigApp(programbase): + ######################################################################## + def __init__(self,parent=None,name=None): + global standalone,isroot + KGlobal.locale().insertCatalogue("guidance") + + if standalone: + KDialogBase.__init__(self,KJanusWidget.Plain,i18n("Disk & Filesystems"), + KDialogBase.User1|KDialogBase.Close, KDialogBase.Close) + self.setButtonText(KDialogBase.User1,i18n("About")) + topwidget = self.plainPage() + else: + KCModule.__init__(self,parent,name) + self.setButtons(0) + self.aboutdata = MakeAboutData() + topwidget = self + + # Create a configuration object. + self.config = KConfig("mountconfigrc") + + KGlobal.iconLoader().addAppDir("guidance") + self.updatingGUI = False + self.mounttable = MountTable('/etc/fstab','/etc/mtab') + self.selectedentry = None + self.aboutus = KAboutApplication(self) + self.sizeviewdialogs = {} + toplayout = QVBoxLayout(topwidget, 0, KDialog.spacingHint()) + #topwidget.setEnabled(isroot) + + hb = QHBox(topwidget) + hb.setSpacing(KDialog.spacingHint()) + #if standalone: + # hb.setMargin(KDialog.marginHint()) + + toplayout.addWidget(hb) + + label = QLabel(hb) + label.setPixmap(UserIcon("kcmpartitions")) + hb.setStretchFactor(label,0) + + label = QLabel(i18n("Available Disks and Filesystems:"),hb) + hb.setStretchFactor(label,1) + + self.mountlist = KListView(topwidget,"Mount list") + toplayout.addWidget(self.mountlist) + self.mountlist.addColumn(i18n("Name")) + self.mountlist.addColumn(i18n("Mount Point")) + self.mountlist.addColumn(i18n("Type")) + self.mountlist.addColumn(i18n("Device")) + self.mountlist.addColumn(i18n("Enabled")) + self.mountlist.setAllColumnsShowFocus(True) + self.mountlist.setSelectionMode(QListView.Single) + self.mountlist.setRootIsDecorated(True) + self.mountlist.setSorting(-1) + self.connect(self.mountlist, SIGNAL("selectionChanged(QListViewItem *)"), self.slotListClicked) + # Doubleclick in item opens modify dialogue. + self.connect(self.mountlist, SIGNAL("doubleClicked(QListViewItem *)"), self.slotModifyClicked) + # Rightclick: Open ContextMenu + self.connect(self.mountlist, SIGNAL("contextMenu(KListView*,QListViewItem*,const QPoint&)"), + self.slotContextMenu) + + hbox = QHBox(topwidget) + toplayout.addWidget(hbox) + hbox.setSpacing(KDialog.spacingHint()) + + toplayout.setStretchFactor(hbox,0) + self.newbutton = KPushButton(i18n("New..."),hbox) + hbox.setStretchFactor(self.newbutton,1) + self.connect(self.newbutton,SIGNAL("clicked()"),self.slotNewClicked) + self.newbutton.setEnabled(isroot) + + self.modifybutton = KPushButton(i18n("Modify..."),hbox) + hbox.setStretchFactor(self.modifybutton,1) + self.connect(self.modifybutton,SIGNAL("clicked()"),self.slotModifyClicked) + + self.deletebutton = KPushButton(i18n("Delete..."),hbox) + hbox.setStretchFactor(self.deletebutton,1) + self.connect(self.deletebutton,SIGNAL("clicked()"),self.slotDeleteClicked) + + self.enablebutton = KPushButton(i18n("Enable"),hbox) + hbox.setStretchFactor(self.enablebutton,1) + self.connect(self.enablebutton,SIGNAL("clicked()"),self.slotEnableClicked) + + self.disablebutton = KPushButton(i18n("Disable"),hbox) + hbox.setStretchFactor(self.disablebutton,1) + self.connect(self.disablebutton,SIGNAL("clicked()"),self.slotDisableClicked) + + self.detailsbutton = KPushButton(i18n("Details..."),hbox) + hbox.setStretchFactor(self.detailsbutton,1) + self.connect(self.detailsbutton,SIGNAL("clicked()"),self.slotDetailsClicked) + + self.devstolistitems = None + self.uuidstolistitems = None + self.mountentriestolistitems = None + self.__updateMountList() + self.__selectEntry(self.mounttable[0]) + + self.configuredialog = MountEntryDialog(None) + + ######################################################################## + def exec_loop(self): + global programbase + self.__loadOptions() + programbase.exec_loop(self) + self.__saveOptions() + + ######################################################################## + def slotContextMenu(self,lv,lv_item,p): + + hal_device = lv_item.haldevice + if hal_device is not None and not isinstance(hal_device,MicroHAL.FakeSystemDevice): + + self.cmenu = KPopupMenu(self,"MyActions") + if isinstance(hal_device,MicroHAL.RemovableDisk) or isinstance(lv_item,MountListViewItem): + self.cmenu.insertItem(i18n("Modify..."), self.slotModifyClicked, 0, 0) + self.cmenu.insertItem(i18n("Delete..."), self.slotDeleteClicked, 0, 1) + if not isroot: + self.cmenu.setItemEnabled(0,False) + self.cmenu.setItemEnabled(1,False) + elif isinstance(hal_device,MicroHAL.Disk) or isinstance(hal_device,MicroHAL.USBDisk): + self.cmenu.insertItem(i18n("Show details..."), self.slotDetailsClicked, 0, 0) + self.cmenu.insertItem(i18n("New..."), self.slotNewClicked, 0, 1) + if not isroot: + self.cmenu.setItemEnabled(1,False) + + self.cmenu.exec_loop(p) + + ######################################################################## + def slotUser1(self): + self.aboutus.show() + + ######################################################################## + def slotEnableClicked(self): + if self.selectedentry!=None: + self.selectedentry.enable(self) + self.mounttable.updateStatus(self.selectedentry) + self.__updateEntry(self.selectedentry) + self.enablebutton.setEnabled(not self.selectedentry.isEnabled()) + self.disablebutton.setEnabled(self.selectedentry.isEnabled()) + + ######################################################################## + def slotDisableClicked(self): + if self.selectedentry!=None: + self.__disableEntry() + + ######################################################################## + def slotModifyClicked(self): + global isroot + if not isroot: + return + + if self.selectedentry!=None: + self.configuredialog.doEditMount(self.mounttable,self.selectedentry) + + lvi = self.mountentriestolistitems[self.selectedentry] + if lvi.hasHAL(): + if lvi.getHAL().getDev()!=self.selectedentry.getDevice(): + # The (device-)item in the listview no longer matches this mount entry. + del self.mountentriestolistitems[self.selectedentry] + lvi.setMountEntry(None) + lvi.updateDisplay() + # Reinsert this mount entry into the list view. + self.__insertMountEntryIntoListView(self.selectedentry) + + elif self.selectedentry.getDevice() is not None \ + and self.selectedentry.getDevice() in self.devstolistitems: + # The mount entry can now merged with any existing (HAL-)item. + # Remove the existing lose item. + self.mountlist.takeItem(lvi) + del self.mountentriestolistitems[self.selectedentry] + del self.devstolistitems[self.selectedentry.getDevice()] + del lvi + # Reinsert this mount entry into the list view. + self.__insertMountEntryIntoListView(self.selectedentry) + + elif self.selectedentry.getUUID() is not None \ + and self.selectedentry.getUUID() in self.uuidstolistitems: + # The mount entry can now merged with any existing (HAL-)item. + # Remove the existing lose item. + self.mountlist.takeItem(lvi) + del self.mountentriestolistitems[self.selectedentry] + del self.uuidstolistitems[self.selectedentry.getUUID()] + del lvi + # Reinsert this mount entry into the list view. + self.__insertMountEntryIntoListView(self.selectedentry) + + self.__updateEntry(self.selectedentry) + self.__selectEntry(self.selectedentry) + else: + self.slotNewClicked() + + ######################################################################## + def slotNewClicked(self): + defaultdevice = None + if self.selectedentry is None: + lvi = self.mountlist.selectedItem() + if lvi is not None and lvi.hasHAL() and (lvi.getMountEntry() is None): + defaultdevice = lvi.getDevice() + newentry = self.configuredialog.doNewMount(self.mounttable,defaultdevice) + if newentry!=None: + self.updatingGUI = True + self.__insertMountEntryIntoListView(newentry) + self.__selectEntry(newentry) + self.updatingGUI = False + + ######################################################################## + def slotDeleteClicked(self): + if self.selectedentry!=None: + if self.selectedentry.isEnabled(): + if not self.__disableEntry(): + return # If we couldn't disable it, then we can't continue. + message = i18n("Are you sure you want to delete mount '%1' of type %2 at '%3'?\n " + + "(This will only remove the mount, no data will be deleted.)") \ + .arg(self.selectedentry.getMountPoint()).arg(self.selectedentry.mounttype).arg( + self.selectedentry.getDevice()) + if KMessageBox.warningYesNo(self,message,i18n("Delete Mount?"))==KMessageBox.Yes: + lvi = self.mountentriestolistitems[self.selectedentry] + if not lvi.hasHAL(): + self.mountlist.takeItem(lvi) + del lvi + del self.mountentriestolistitems[self.selectedentry] + else: + lvi.setMountEntry(None) + self.mounttable.remove(self.selectedentry) + self.mounttable.updateFstabOnDisk() + self.__selectEntry(None) + + ######################################################################## + def slotDetailsClicked(self): + # Popup a dialog showing disklayout and a graphical represenation of 'df' + hal_device = self.mountlist.selectedItem().haldevice + if isinstance(hal_device,MicroHAL.Disk): + blk = hal_device.getDev() + devicepath, devicename = ('/'.join(blk.split('/')[0:-1])+'/', blk.split('/')[-1]) + # We keep a dict with those widgets, that saves us some time reading out all the values. + if devicename not in self.sizeviewdialogs.keys(): + self.sizeviewdialogs[devicename] = sizeview.SizeView(self,devicename,devicepath) + self.sizeviewdialogs[devicename].exec_loop() + else: + self.sizeviewdialogs[devicename].exec_loop() + else: + print "Sizeview doesn't support",blk.__class__," yet." + + ######################################################################## + def slotListClicked(self,item): + if self.updatingGUI==False: + self.__selectEntry(item.getMountEntry()) + + ######################################################################## + def __disableEntry(self): + self.selectedentry.disable(self) + self.mounttable.updateStatus(self.selectedentry) + self.__updateEntry(self.selectedentry) + self.enablebutton.setEnabled(not self.selectedentry.isEnabled() and self.selectedentry.isFileSystemAvailable()) + self.disablebutton.setEnabled(self.selectedentry.isEnabled()) + return not self.selectedentry.isEnabled() + + ######################################################################## + def __updateEntry(self,selectedentry): + # Update the display. + lvi = self.mountentriestolistitems[selectedentry] + lvi.updateDisplay() + + ######################################################################## + def __loadOptions(self): + self.config.setGroup("General") + size = self.config.readSizeEntry("Geometry") + if size.isEmpty()==False: + self.resize(size) + + ####################################################################### + def __saveOptions(self): + global isroot + if isroot: + return + self.config.setGroup("General") + self.config.writeEntry("Geometry", self.size()) + self.config.sync() + + ######################################################################## + def __updateMountList(self): + self.mountentriestolistitems = {} + + self.mountlist.clear() + + self.listgroups = {} + self.devstolistitems = {} + self.uuidstolistitems = {} + + lasttopitem = None + + # Find out which disks are installed and should be shown in the + # listview. For real disks we put a 'group' in the listview and + # under the group we list the partitions, whether they are + # mounted or not. + for blockdevice in microhal.getDevices(): + # We are looking for block devices that represent hard disks or + # things that have partitions and are not removable + if (blockdevice.major in microhal.partitionblockdevs and not blockdevice.removable) \ + or isinstance(blockdevice,MicroHAL.USBDisk): + + # We have a not removable block device. + # We want to create a listitem for the device and subitems + # for each partition. + groupitem = MountGroupListViewItem(self.mountlist,blockdevice) + groupitem.setOpen(True) + lasttopitem = groupitem + lvi = None + for partition in blockdevice.getPartitions(): + # Try to find a matching mount entry for this partition. + lastlvi = lvi + lvi = MountListViewItem(groupitem,None,partition) + + if partition.getUUID() is not None: + self.uuidstolistitems[partition.getUUID()] = lvi + if partition.getDev() is not None: + self.devstolistitems[partition.getDev()] = lvi + + if lastlvi is not None: + lvi.moveItem(lastlvi) + elif blockdevice.getMajor() in microhal.cdromsdevs or blockdevice.isRemovable(): + # Removable block device, assume CDROM (even if it's a partitionblockdevice) + lvi = MountListViewItem(self.mountlist,None,blockdevice) + if blockdevice.getUUID() is not None: + self.uuidstolistitems[blockdevice.getUUID()] = lvi + if blockdevice.getDev() is not None: + self.devstolistitems[blockdevice.getDev()] = lvi + lasttopitem = lvi + + + systemdevice = MicroHAL.FakeSystemDevice() + systemdevice.iconname = systemdevice.getIconName() + groupitem = MountGroupListViewItem(self.mountlist,systemdevice) + + if lasttopitem is not None: + groupitem.moveItem(lasttopitem) + lasttopitem = groupitem + + self.listgroups["system"] = groupitem + + self.mountentriestolistitems = {} + for entry in self.mounttable: + self.__insertMountEntryIntoListView(entry) + + ######################################################################## + def __insertMountEntryIntoListView(self,mountentry): + if mountentry.getDevice() in self.devstolistitems: + lvi = self.devstolistitems[mountentry.getDevice()] + lvi.setMountEntry(mountentry) + elif mountentry.getUUID() in self.uuidstolistitems: + lvi = self.uuidstolistitems[mountentry.getUUID()] + lvi.setMountEntry(mountentry) + else: + cat = mountentry.getCategory() # Place it under a special node? + if cat not in self.listgroups: + lvi = MountListViewItem(self.mountlist,mountentry) + item = self.mountlist.firstChild() + else: + lvi = MountListViewItem(self.listgroups[cat],mountentry) + item = self.listgroups[cat].firstChild() + + # Move the item to the end of this (sub-list). + while item.nextSibling() is not None: + item = item.nextSibling() + lvi.moveItem(item) + + self.mountentriestolistitems[mountentry] = lvi + + ######################################################################## + def __selectEntry(self,mountentry): + if mountentry is not None and isroot: + lvi = self.mountentriestolistitems[mountentry] + self.mountlist.setSelected(lvi,True) + self.enablebutton.setEnabled(not mountentry.isEnabled() and mountentry.isFileSystemAvailable()) + self.selectedentry = mountentry + # disable unsupported stuff, such as SystemEntries that canot be disabled and modified + if not mountentry.maydisable: + disable = False + else: + disable = mountentry.isEnabled() + if mountentry.notInFstab: + delete = False + modify = False + else: + delete = True + modify = True + + self.disablebutton.setEnabled(disable) + self.deletebutton.setEnabled(delete) + self.modifybutton.setEnabled(modify) + + else: + self.enablebutton.setEnabled(False) + self.disablebutton.setEnabled(False) + self.deletebutton.setEnabled(False) + self.modifybutton.setEnabled(False) + self.detailsbutton.setEnabled(False) + self.selectedentry = None + selected_item = self.mountlist.selectedItem() + if selected_item is not None: + self.detailsbutton.setEnabled(isinstance(selected_item.haldevice,MicroHAL.Disk) \ + and not isinstance(selected_item.haldevice,MicroHAL.RemovableDisk)) + else: + self.detailsbutton.setEnabled(False) + + ####################################################################### + # KControl virtual void methods + def load(self): + pass + def save(self): + pass + def defaults(self): + pass + def sysdefaults(self): + pass + + def aboutData(self): + # Return the KAboutData object which we created during initialisation. + return self.aboutdata + def buttons(self): + # Only supply a Help button. Other choices are Default and Apply. + return KCModule.Help + +############################################################################ +# Factory function for KControl +def create_mountconfig(parent,name): + global kapp, microhal + microhal = MicroHAL.MicroHAL() + kapp = KApplication.kApplication() + return MountConfigApp(parent, name) + +############################################################################ +def MakeAboutData(): + aboutdata = KAboutData("mountconfig",programname,version,"Disk & Filesystem Configuration Tool", + KAboutData.License_GPL, "Copyright (C) 2003-2007 Simon Edwards") + aboutdata.addAuthor("Simon Edwards","Developer","simon@simonzone.com", + "http://www.simonzone.com/software/guidance") + aboutdata.addAuthor("Sebastian Kügler","Developer","sebas@kde.org","http://vizZzion.org"); + return aboutdata + +if standalone: + aboutdata = MakeAboutData() + KCmdLineArgs.init(sys.argv,aboutdata) + + microhal = MicroHAL.MicroHAL() + kapp = KApplication() + sysvapp = MountConfigApp() + sysvapp.exec_loop() diff --git a/mountconfig/sizeview.py b/mountconfig/sizeview.py new file mode 100644 index 0000000..bbb1f5b --- /dev/null +++ b/mountconfig/sizeview.py @@ -0,0 +1,504 @@ +#!/usr/bin/env python + +from qt import * +from kdecore import * +import sys, os + +def getLabel(blocks): + """ Translates blocksize into human readable labels, such as 17.3 Gb, 2.1 Mb. """ + + try: + blocks = int(blocks) # 1K blocks now. + except ValueError: + return i18n("n/a") + if blocks<1024: + return i18n("%1 Kb").arg(blocks) + if blocks<1024*1024: + return i18n("%1 Mb").arg(round(float(blocks)/1024.0,1)) + blocks /= 1024 + if blocks<1024*1024: + return i18n("%1 Gb").arg(round(float(blocks)/1024.0,1)) + blocks /= 1024 + return i18n("%1 Tb").arg(round(float(blocks)/1024.0,1)) + + +class SizeViewApplication(QApplication): + """ Boilerplate """ + def __init__(self,devicename,devicepath,args=[]): + QApplication.__init__(self,args) + + self.maindialog = SizeView(None,devicename,devicepath) + self.setMainWidget(self.maindialog) + self.maindialog.show() + self.exec_loop() + +class SizeView(QDialog): + """ A SizeView represents a horizontal list of PartitionGroupWidgets. + It supplies the code to read the sizes and the values that have + to be filled in, using the /proc filesystem and the program "df". + """ + + dev_path = "/dev/" # Where to look for the partitions + devicename = "" # Such as hda1 + partitions = {} # List of partitions + sizes = {} # Maps devicenames to blocksizes + mountpoints = {} # Maps devicenames to mountpoints + used = {} # Blocks used on a partition + available = {} # Blocks available + part_types = {} # Maps devicenames to partitiontypes + partitionwidgets = [] # Holds a list of the PartitionGroup widgets + + def __init__(self,parent,devicename,devicepath=None): + self.partitionwidgets = [] + QDialog.__init__(self,None,None,0,0) + self.dialogtitle = i18n("Diskspace & Partitions") + self.setCaption(self.dialogtitle) + self.devicename = devicename + if devicepath: + self.dev_path = devicepath + + # Retrieve all information from the system. + self.readMounts() + self.readSize() + self.readSwaps() + + partitions = self.partitions.keys() + partitions.sort() + + number=1 + for part in partitions: + try: + fill = self.sizes[part] + mountpoint = self.mountpoints[part] + used = self.used[part] + available = self.available[part] + except KeyError: + # Handles empty or not-mounted partitions + fill = None + mountpoint = i18n("n/a") + used = str(i18n("n/a")) + available = str(i18n("n/a")) + + pwidg = PartitionGroup(part,self,fill,number,self.part_types,self.dev_path) + pwidg.setSize(self.partitions[part]) + pwidg.setMountPoint(mountpoint) + pwidg.setUsed(used) + pwidg.setAvailable(available) + pwidg.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding,QSizePolicy.MinimumExpanding,0,0, + pwidg.sizePolicy().hasHeightForWidth())) + self.partitionwidgets.append(pwidg) + number += 1 + + n = len(partitions) + r = 2 + c = 0 + cols = 1 + + # Compute number of rows needed for partitions. + if n%cols > 0: + rows = int(n/cols)+1 + else: + rows = int(n/cols) + if n is 1: rows = 2 + + # Build main Gridlayout. + total_rows = rows+2 + self.grid = QGridLayout(self,total_rows,2,5) + #self.setSizeGripEnabled(1) + + self.buttonCancel = QPushButton(i18n("Close"),self,"buttonCancel") + self.buttonCancel.setAutoDefault(1) + self.buttonCancel.setFixedWidth(80) + self.grid.addWidget(self.buttonCancel,total_rows-1,1,Qt.AlignRight) + + self.grid.setRowStretch(0,0) + self.grid.setRowStretch(total_rows-1,0) + + # Stretch all but first and last rows. + for row in range(1,total_rows-1): + self.grid.setRowStretch(row,5) + + self.clearWState(Qt.WState_Polished) + self.connect(self.buttonCancel,SIGNAL("clicked()"),self.hide) + + #self.mainlabel = QLabel("<font size=+2><b>"+self.dialogtitle+"</b></font>",self) + #self.grid.addWidget(self.mainlabel,0,0) + + self.diskgroup = DiskGroup(self,self.devicename,self.dev_path,self.partitions,self.totalsize,self.mountpoints) + self.grid.addMultiCellWidget(self.diskgroup,1,1,0,1) + + for pw in self.partitionwidgets: + self.grid.addWidget(pw,r,c) + if c is cols: + r += 1 + c = 0 + else: + c += 1 + + def readSize(self): + fhandle = open("/proc/partitions","r") + self.partitions = {} + self.totalsize = 0 + for line in fhandle.readlines(): + try: + major,minor,blocks,name = line.split() + if name == self.devicename: + self.totalsize = blocks + if name[:len(self.devicename)] == self.devicename and len(name) > len(self.devicename): + self.partitions[name] = blocks + except ValueError: + pass + fhandle.close() + + def readMounts(self): + fhandle = os.popen("/bin/df") + for l in fhandle.readlines(): + v = l.split() + try: + p,s = v[0].split("/")[2],v[4][:-1] + self.sizes[p] = s + self.mountpoints[p] = v[5] + self.used[p] = v[2] + self.available[p] = v[3] + self.part_types[p] = "filesystem" + except IndexError: + pass + fhandle.close() + + def readSwaps(self): + fhandle = open("/proc/swaps") + for line in fhandle.readlines(): + try: + device,type,size,used,priority = line.split() + device = device[len(self.dev_path):] + self.used[device] = used + self.sizes[device] = round(float(used)/float(size)*100 ,1) + self.available[device] = str(int(size)-int(used)) + self.mountpoints[device] = "swap" + self.part_types[device] = "swap" + except: + pass + fhandle.close() + + """ + def __show__(self): + print self.partitions + print "Device", self.devicename, self.totalsize + for p in self.partitions: + print p, self.partitions[p], self.partitions[p] + """ +class DiskGroup(QGroupBox): + """ Shows an overview of the physical layout of the disks, with the different partitions on it. """ + + def __init__(self,parent,device,dev_path,partitions,totalsize,mountpoints): + + QGroupBox.__init__(self,parent,"DiskViewGroup") + self.setTitle(i18n("Disk %1%2").arg(dev_path).arg(device)) + self.mountpoints = mountpoints + self.partitions = partitions + self.totalsize = totalsize + + self.setColumnLayout(0,Qt.Vertical) + self.layout().setSpacing(6) + self.layout().setMargin(11) + DiskViewGroupLayout = QVBoxLayout(self.layout()) + DiskViewGroupLayout.setAlignment(Qt.AlignTop) + colors = ["dark orange","dodger blue","gold","green","firebrick","navy","darkorange","darkblue"] + + self.diskview = DiskView(self,self.percentages(),colors) + self.diskview.setScaledContents(1) + DiskViewGroupLayout.addWidget(self.diskview) + + parts = self.partitions.keys() + parts.sort() + self.percentages() + + cols = 3 # Number of columns to use for colorlabels. + rows = len(parts)/cols + mod = len(parts)%cols + if mod > 0: + rows += cols-mod + + # We multiply the number of cols by 3, first for the colorlabel, second for the name, third for spacing. + cols = cols*3 + DiskViewPartitionListLayout = QGridLayout(DiskViewGroupLayout,rows,cols) + + i = cl = r = c = 0 + ps = ls = {} + for dev in parts: + ps[i] = LegendLabel(self,colors[cl]) + DiskViewPartitionListLayout.addWidget(ps[i],r,c) + try: + lbl = self.mountpoints[dev] + except KeyError: + lbl = "not mounted" + ls[i] = QLabel(self,lbl+'<br /> ('+dev_path+dev+')',self) + DiskViewPartitionListLayout.addWidget(ls[i],r,c+1) + cl += 1 + if cl == len(colors): + cl = 0 + i += 1 + if c is cols: + c = 0 + r += 1 + else: + c += 3 + + def percentages(self): + + p_t = 0 + for p in self.partitions.values(): + p_t += int(p) + + self.perc = {} + for p in self.partitions.keys(): + self.perc[p] = float(float(self.partitions[p])/float(p_t)) + return self.perc + + +class PartitionGroup(QGroupBox): + """ Represents a groupbox with the filled bar and a couple of labels with + information about the partition in it.""" + + blocksize = 0 + title = str(i18n("Partition")) + + def __init__(self,device,parent,fill_percent,number,part_types,dev_path): + QGroupBox.__init__(self,parent) + self.part_types = part_types + self.dev_path = dev_path + self.setGeometry(QRect(110,100,370,203)) + self.setColumnLayout(0,Qt.Vertical) + self.layout().setSpacing(3) + self.layout().setMargin(5) + self.setMinimumSize(280,120) + + partitiongroup_layout = QGridLayout(self.layout()) + partitiongroup_layout.setAlignment(Qt.AlignTop) + self.available = QLabel(i18n("available"),self) + partitiongroup_layout.addWidget(self.available,3,4) + + self.device = QLabel(i18n("device"),self) + partitiongroup_layout.addMultiCellWidget(self.device,1,1,3,4) + + self.partpixmap = PartitionView(self,fill_percent,self.part_types,device) + self.partpixmap.setScaledContents(1) + + partitiongroup_layout.addMultiCellWidget(self.partpixmap,0,0,0,4) + self.textLabel1_3 = QLabel("textLabel1_3",self) + partitiongroup_layout.addWidget(self.textLabel1_3,3,0) + self.totalsize = QLabel("totalsize",self) + partitiongroup_layout.addWidget(self.totalsize,2,1) + self.textLabel1_2 = QLabel(self,"textLabel1_2") + partitiongroup_layout.addWidget(self.textLabel1_2,2,0) + self.textLabel1 = QLabel(self,"textLabel1") + partitiongroup_layout.addWidget(self.textLabel1,1,0) + self.textLabel3_2 = QLabel(self,"textLabel3_2") + partitiongroup_layout.addMultiCellWidget(self.textLabel3_2,2,2,2,3) + self.percentfilled = QLabel(self,"percentfree") + partitiongroup_layout.addWidget(self.percentfilled,2,4) + self.textLabel3_3 = QLabel(self,"textLabel3_3") + partitiongroup_layout.addWidget(self.textLabel3_3,3,2) + self.textLabel3 = QLabel(self,"textLabel3") + partitiongroup_layout.addWidget(self.textLabel3,1,2) + self.used = QLabel(self,"used") + partitiongroup_layout.addWidget(self.used,3,1) + self.mountpoint = QLabel(self,"mountpoint") + self.mountpoint.setMinimumSize(QSize(60,0)) + partitiongroup_layout.addWidget(self.mountpoint,1,1) + self.clearWState(Qt.WState_Polished) + + self.setTitle(i18n("%1. Partition").arg(number)) + self.textLabel1_3.setText(i18n("Used:")) + self.textLabel1_2.setText(i18n("Total Size:")) + self.textLabel1.setText(i18n("Mountpoint:")) + self.textLabel3_2.setText(i18n("% Used:")) + self.textLabel3_3.setText(i18n("Available:")) + self.textLabel3.setText(i18n("Device:")) + + self.setDevice(self.dev_path+device) + self.setFillPercentage(fill_percent) + + def setSize(self,label): + self.totalsize.setText(getLabel(label)) + + def setDevice(self,device): + self.device.setText(device) + + def setMountPoint(self,mountpoint): + self.mountpoint.setText(mountpoint) + self.setTitle(i18n("Partition %1").arg(mountpoint)) + + def setTotalSize(self,totalsize): + self.totalsize.setText(getLabel(totalsize)) + + def setFillPercentage(self,fill_percent): + self.fill_percent = self.partpixmap.fill_percent = fill_percent + if fill_percent is not None: + self.percentfilled.setText("%s%%" % fill_percent) + else: + self.percentfilled.setText(i18n("Unknown")) + + def setUsed(self,used): + self.used.setText(getLabel(used)) + + def setAvailable(self,available): + self.available.setText(getLabel(available)) + +class LegendLabel(QLabel): + """ Show some color in the DiskView legend """ + + def __init__(self,parent,color="green",style=QBrush.SolidPattern): + QLabel.__init__(self,parent,"bla") + self.w = 40 + self.h = 20 + self.pmsize = QSize(self.w,self.h) + self.pm = QPixmap(self.pmsize) + self.linewidth = 2 + self.color = QColor(color) + self.style = style + self.framecolor = QColor("black") + self.paintMe() + self.setPixmap(self.pm) + self.setScaledContents(1) + self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0, + self.sizePolicy().hasHeightForWidth())) + + def paintMe(self): + p = QPainter(self.pm) + p.fillRect(0,0,self.w,self.h,QBrush(self.color,self.style)) + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,self.w-self.linewidth/2,self.h-self.linewidth/2) + p.end() + +class PartitionView(QLabel): + """ PartitionView is a label carryig a pixmap. This class's main purpose is handlig layout + of the underlying pixmap.""" + w = 250 + h = 35 + def __init__(self,parent,fill_percent,part_types,device): + self.part_types = part_types + self.fill_percent = fill_percent + QLabel.__init__(self,parent,"pview") + self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0, + self.sizePolicy().hasHeightForWidth())) + self.setMinimumSize(QSize(self.w,self.h)) + self.setPixmap(PartitionPixmap(QSize(self.w,self.h),self.fill_percent,self.part_types,device)) + self.setScaledContents(1) + self.setAlignment(QLabel.AlignCenter) + +class DiskView(PartitionView): + """ PartitionView is a label carryig a pixmap. This class's main purpose is handlig layout + of the underlying pixmap.""" + + w = 540 + h = 50 + linewidth = 2 + + def __init__(self,parent,percents,colors): + QLabel.__init__(self,parent) + + self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding,0,0, + self.sizePolicy().hasHeightForWidth())) + self.setPixmap(DiskPixmap(percents,colors,(self.w,self.h))) + self.setScaledContents(1) + self.setAlignment(QLabel.AlignCenter) + +class DiskPixmap(QPixmap): + + linewidth = 2 # Width of surrounding frame + + def __init__(self,percents,colors,(w,h)): + self.percents = percents + self.w,self.h = w,h + self.colors = colors + QPixmap.__init__(self,w,h) + self.paintMe() + + def paintMe(self): + p = QPainter(self) + w,h = self.w,self.h + i = 0 + x0 = 0 + y = 0 + + # Paint background, this is interesting for empty partitions. + p.fillRect(0,0,w,h,QBrush(QColor("white"))) + + parts = self.percents.keys() + parts.sort() + xa = wa = 0 + for part in parts: + W = (w * self.percents[part]) + # We need to adjust a little to avoid to get wholes. + if x0>0: xa = 2 + if W < self.w: wa = 2 + p.fillRect(x0-xa,0,W+wa,h,QBrush(QColor(self.colors[i]))) + i += 1 + x0 += W + + # Paint Frame around it. + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,self.width()-self.linewidth/2,self.height()-self.linewidth/2) + p.end() + + +class PartitionPixmap(QPixmap): + """ A PartitionPixmap is a two colored bar with a black frame. The first color represents the + percentage that's used, the second one the free percentage.""" + linewidth = 2 # Width of surrounding frame + + def __init__(self,pmsize,fill_percent,part_types,device): + QPixmap.__init__(self,pmsize) + + self.pmsize = pmsize # Size of the pixmap + self.part_types = part_types # Array to look up the type of the partition + self.fill_percent = fill_percent + self.device = device # Device name of the partition + + self.w = self.pmsize.width() + self.h = self.pmsize.height() + self.paintMe() + + def paintMe(self): + p = QPainter(self) + try: + fill_percent = int(self.fill_percent) + if self.part_types[self.device] == "swap": + # Swap partitions get blueish colors. + color_used = QColor("blue") + color_free = QColor("lightblue") + else: + # Regular partitions get a red / green color combo. + color_used = QColor("red") + color_free = QColor("forest green") + except (KeyError,TypeError): + # Partition has no fillsize, might be empty or not mounted partition + p.fillRect(0,0,self.w,self.h,QBrush(QColor("darkgrey"))) + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,self.w-self.linewidth/2,self.h-self.linewidth/2) + p.end() + return + # Total width of the pixmap + W,H = float(self.w),float(self.h) + + # Paint filled == red part of the bar. + x = y = 0 + w = W - (W*(1-(fill_percent/100.00))) + h = H + p.fillRect(x,y,w,h,QBrush(color_used)) + + # Paint green part == space left + x = w + w = W - w + p.fillRect(x,y,w,h,QBrush(color_free)) + + # Paint Frame around it. + p.setPen(QPen(QColor("black"),self.linewidth)) + p.drawRect(self.linewidth/2,self.linewidth/2,W-self.linewidth/2,H-self.linewidth/2) + + p.end() + +if __name__ == "__main__": + device = "sdc" + app = SizeViewApplication(device,None,sys.argv) |