summaryrefslogtreecommitdiffstats
path: root/mountconfig
diff options
context:
space:
mode:
Diffstat (limited to 'mountconfig')
-rwxr-xr-xmountconfig/MicroHAL.py884
-rw-r--r--mountconfig/SMBShareSelectDialog.py573
-rw-r--r--mountconfig/SimpleCommandRunner.py69
-rw-r--r--mountconfig/fuser.py299
-rw-r--r--mountconfig/fuser_ui.ui352
-rw-r--r--mountconfig/mountconfig.desktop51
-rwxr-xr-xmountconfig/mountconfig.py3303
-rw-r--r--mountconfig/sizeview.py504
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>&gt;&gt;&gt; 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)