allow styleable TemplatedMultiContent lists
[enigma2.git] / lib / python / Components / Harddisk.py
1 from os import system, listdir, statvfs, popen, makedirs, readlink, stat, major, minor
2 from Tools.Directories import SCOPE_HDD, resolveFilename
3 from Tools.CList import CList
4 from SystemInfo import SystemInfo
5
6 def tryOpen(filename):
7         try:
8                 procFile = open(filename)
9         except IOError:
10                 return ""
11         return procFile
12
13 class Harddisk:
14         def __init__(self, device):
15                 self.device = device
16                 procfile = tryOpen("/sys/block/"+self.device+"/dev")
17                 tmp = procfile.readline().split(':')
18                 s_major = int(tmp[0])
19                 s_minor = int(tmp[1])
20                 for disc in listdir("/dev/discs"):
21                         path = readlink('/dev/discs/'+disc)
22                         devidex = '/dev/discs/'+disc+'/'
23                         devidex2 = '/dev'+path[2:]+'/'
24                         disc = devidex2+'disc'
25                         ret = stat(disc).st_rdev
26                         if s_major == major(ret) and s_minor == minor(ret):
27                                 self.devidex = devidex
28                                 self.devidex2 = devidex2
29                                 print "new Harddisk", device, '->', self.devidex, '->', self.devidex2
30                                 break
31
32         def __lt__(self, ob):
33                 return self.device < ob.device
34
35         def bus(self):
36                 ide_cf = self.device.find("hd") == 0 and self.devidex2.find("host0") == -1 # 7025 specific
37                 internal = self.device.find("hd") == 0
38                 if ide_cf:
39                         ret = "External (CF)"
40                 elif internal:
41                         ret = "Internal"
42                 else:
43                         ret = "External"
44                 return ret
45
46         def diskSize(self):
47                 procfile = tryOpen("/sys/block/"+self.device+"/size")
48                 if procfile == "":
49                         return 0
50                 line = procfile.readline()
51                 procfile.close()
52                 try:
53                         cap = int(line)
54                 except:
55                         return 0;
56                 return cap / 1000 * 512 / 1000
57
58         def capacity(self):
59                 cap = self.diskSize()
60                 if cap == 0:
61                         return ""
62                 return "%d.%03d GB" % (cap/1024, cap%1024)
63
64         def model(self):
65                 if self.device.find("hd") == 0:
66                         procfile = tryOpen("/proc/ide/"+self.device+"/model")
67                         if procfile == "":
68                                 return ""
69                         line = procfile.readline()
70                         procfile.close()
71                         return line.strip()
72                 elif self.device.find("sd") == 0:
73                         procfile = tryOpen("/sys/block/"+self.device+"/device/vendor")
74                         if procfile == "":
75                                 return ""
76                         vendor = procfile.readline().strip()
77                         procfile.close()
78                         procfile = tryOpen("/sys/block/"+self.device+"/device/model")
79                         model = procfile.readline().strip()
80                         return vendor+'('+model+')'
81                 else:
82                         assert False, "no hdX or sdX"
83
84         def free(self):
85                 procfile = tryOpen("/proc/mounts")
86                 
87                 if procfile == "":
88                         return -1
89
90                 free = -1
91                 while 1:
92                         line = procfile.readline()
93                         if line == "":
94                                 break
95                         if line.startswith(self.devidex):
96                                 parts = line.strip().split(" ")
97                                 try:
98                                         stat = statvfs(parts[1])
99                                 except OSError:
100                                         continue
101                                 free = stat.f_bfree/1000 * stat.f_bsize/1000
102                                 break
103                 procfile.close()
104                 return free
105
106         def numPartitions(self):
107                 try:
108                         idedir = listdir(self.devidex)
109                 except OSError:
110                         return -1
111                 numPart = -1
112                 for filename in idedir:
113                         if filename.startswith("disc"):
114                                 numPart += 1
115                         if filename.startswith("part"):
116                                 numPart += 1
117                 return numPart
118
119         def unmount(self):
120                 procfile = tryOpen("/proc/mounts")
121
122                 if procfile == "":
123                         return -1
124
125                 cmd = "/bin/umount"
126
127                 for line in procfile:
128                         if line.startswith(self.devidex):
129                                 parts = line.split()
130                                 cmd = ' '.join([cmd, parts[1]])
131
132                 procfile.close()
133
134                 res = system(cmd)
135                 return (res >> 8)
136
137         def createPartition(self):
138                 cmd = "/sbin/sfdisk -f " + self.devidex + "disc"
139                 sfdisk = popen(cmd, "w")
140                 sfdisk.write("0,\n;\n;\n;\ny\n")
141                 sfdisk.close()
142                 return 0
143
144         def mkfs(self):
145                 cmd = "/sbin/mkfs.ext3 "
146                 if self.diskSize() > 4 * 1024:
147                         cmd += "-T largefile "
148                 cmd += "-m0 " + self.devidex + "part1"
149                 res = system(cmd)
150                 return (res >> 8)
151
152         def mount(self):
153                 cmd = "/bin/mount -t ext3 " + self.devidex + "part1"
154                 res = system(cmd)
155                 return (res >> 8)
156
157         def createMovieFolder(self):
158                 try:
159                         makedirs(resolveFilename(SCOPE_HDD))
160                 except OSError:
161                         return -1
162                 return 0
163
164         def fsck(self):
165                 # We autocorrect any failures
166                 # TODO: we could check if the fs is actually ext3
167                 cmd = "/sbin/fsck.ext3 -f -p " + self.devidex + "part1"
168                 res = system(cmd)
169                 return (res >> 8)
170
171         errorList = [ _("Everything is fine"), _("Creating partition failed"), _("Mkfs failed"), _("Mount failed"), _("Create movie folder failed"), _("Fsck failed"), _("Please Reboot"), _("Filesystem contains uncorrectable errors"), _("Unmount failed")]
172
173         def initialize(self):
174                 self.unmount()
175
176                 if self.createPartition() != 0:
177                         return -1
178
179                 if self.mkfs() != 0:
180                         return -2
181
182                 if self.mount() != 0:
183                         return -3
184
185                 if self.createMovieFolder() != 0:
186                         return -4
187
188                 return 0
189
190         def check(self):
191                 self.unmount()
192
193                 res = self.fsck()
194                 if res & 2 == 2:
195                         return -6
196
197                 if res & 4 == 4:
198                         return -7
199
200                 if res != 0 and res != 1:
201                         # A sum containing 1 will also include a failure
202                         return -5
203
204                 if self.mount() != 0:
205                         return -3
206
207                 return 0
208         
209         def getDeviceDir(self):
210                 return self.devidex
211         
212         def getDeviceName(self):
213                 return self.getDeviceDir() + "disc"
214
215 class Partition:
216         def __init__(self, mountpoint, description = "", force_mounted = False):
217                 self.mountpoint = mountpoint
218                 self.description = description
219                 self.force_mounted = force_mounted
220                 self.is_hotplug = force_mounted # so far; this might change.
221
222         def stat(self):
223                 return statvfs(self.mountpoint)
224
225         def free(self):
226                 try:
227                         s = self.stat()
228                         return s.f_bavail * s.f_bsize
229                 except OSError:
230                         return None
231         
232         def total(self):
233                 try:
234                         s = self.stat()
235                         return s.f_blocks * s.f_bsize
236                 except OSError:
237                         return None
238
239         def mounted(self):
240                 # THANK YOU PYTHON FOR STRIPPING AWAY f_fsid.
241                 # TODO: can os.path.ismount be used?
242                 if self.force_mounted:
243                         return True
244                 procfile = tryOpen("/proc/mounts")
245                 for n in procfile.readlines():
246                         if n.split(' ')[1] == self.mountpoint:
247                                 return True
248                 return False
249
250 class HarddiskManager:
251         def __init__(self):
252                 self.hdd = [ ]
253                 self.cd = ""
254                 self.partitions = [ ]
255                 
256                 self.on_partition_list_change = CList()
257                 
258                 self.enumerateBlockDevices()
259                 
260                 # currently, this is just an enumeration of what's possible,
261                 # this probably has to be changed to support automount stuff.
262                 # still, if stuff is mounted into the correct mountpoints by
263                 # external tools, everything is fine (until somebody inserts 
264                 # a second usb stick.)
265                 p = [
266                                         ("/media/hdd", _("Harddisk")), 
267                                         ("/media/card", _("Card")), 
268                                         ("/media/cf", _("Compact Flash")),
269                                         ("/media/mmc1", _("MMC Card")),
270                                         ("/media/net", _("Network Mount")),
271                                         ("/media/ram", _("Ram Disk")),
272                                         ("/media/usb", _("USB Stick")),
273                                         ("/", _("Internal Flash"))
274                                 ]
275                 
276                 for x in p:
277                         self.partitions.append(Partition(mountpoint = x[0], description = x[1]))
278
279         def getBlockDevInfo(self, blockdev):
280                 devpath = "/sys/block/" + blockdev
281                 error = False
282                 removable = False
283                 blacklisted = False
284                 is_cdrom = False
285                 partitions = []
286                 try:
287                         removable = bool(int(open(devpath + "/removable").read()))
288                         dev = int(open(devpath + "/dev").read().split(':')[0])
289                         if dev in [7, 31]: # loop, mtdblock
290                                 blacklisted = True
291                         if blockdev[0:2] == 'sr':
292                                 is_cdrom = True
293                         if blockdev[0:2] == 'hd':
294                                 try:
295                                         media = open("/proc/ide/%s/media" % blockdev).read()
296                                         if media.find("cdrom") != -1:
297                                                 is_cdrom = True
298                                 except IOError:
299                                         error = True
300                         # check for partitions
301                         if not is_cdrom:
302                                 for partition in listdir(devpath):
303                                         if partition[0:len(blockdev)] != blockdev:
304                                                 continue
305                                         partitions.append(partition)
306                         else:
307                                 self.cd = blockdev
308                 except IOError:
309                         error = True
310                 return error, blacklisted, removable, is_cdrom, partitions
311
312         def enumerateBlockDevices(self):
313                 print "enumerating block devices..."
314                 for blockdev in listdir("/sys/block"):
315                         error, blacklisted, removable, is_cdrom, partitions = self.getBlockDevInfo(blockdev)
316                         print "found block device '%s':" % blockdev, 
317                         if error:
318                                 print "error querying properties"
319                         elif blacklisted:
320                                 print "blacklisted"
321                         else:
322                                 print "ok, removable=%s, cdrom=%s, partitions=%s, device=%s" % (removable, is_cdrom, partitions, blockdev)
323                                 self.addHotplugPartition(blockdev, blockdev)
324                                 for part in partitions:
325                                         self.addHotplugPartition(part, part)
326
327         def getAutofsMountpoint(self, device):
328                 return "/autofs/%s/" % (device)
329
330         def addHotplugPartition(self, device, description):
331                 p = Partition(mountpoint = self.getAutofsMountpoint(device), description = description, force_mounted = True)
332                 self.partitions.append(p)
333                 self.on_partition_list_change("add", p)
334                 l = len(device)
335                 if l and device[l-1] not in ('0','1','2','3','4','5','6','7','8','9'):
336                         error, blacklisted, removable, is_cdrom, partitions = self.getBlockDevInfo(device)
337                         if not blacklisted and not removable and not is_cdrom:
338                                 self.hdd.append(Harddisk(device))
339                                 self.hdd.sort()
340                                 SystemInfo["Harddisc"] = len(self.hdd) > 0
341
342         def removeHotplugPartition(self, device):
343                 mountpoint = self.getAutofsMountpoint(device)
344                 for x in self.partitions[:]:
345                         if x.mountpoint == mountpoint:
346                                 self.partitions.remove(x)
347                                 self.on_partition_list_change("remove", x)
348                 l = len(device)
349                 if l and device[l-1] not in ('0','1','2','3','4','5','6','7','8','9'):
350                         idx = 0
351                         for hdd in self.hdd:
352                                 if hdd.device == device:
353                                         del self.hdd[idx]
354                                         break
355                         SystemInfo["Harddisc"] = len(self.hdd) > 0
356
357         def HDDCount(self):
358                 return len(self.hdd)
359
360         def HDDList(self):
361                 list = [ ]
362                 for hd in self.hdd:
363                         hdd = hd.model() + " - " + hd.bus()
364                         cap = hd.capacity()
365                         if cap != "":
366                                 hdd += " (" + cap + ")"
367                         list.append((hdd, hd))
368                 return list
369
370         def getCD(self):
371                 return self.cd
372
373         def getMountedPartitions(self, onlyhotplug = False):
374                 return [x for x in self.partitions if (x.is_hotplug or not onlyhotplug) and x.mounted()]
375
376 harddiskmanager = HarddiskManager()