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