chiark / gitweb /
RC1
[cura.git] / Cura / util / removableStorage.py
1 """
2 This module handles detection and clean ejection of removable storage. (Mainly SD cards)
3 This is OS depended.
4         On windows it looks for removable storage drives.
5         On MacOS it specificly looks for SD cards.
6         On Linux it looks for anything mounted in /media/
7 """
8 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
9 import platform
10 import string
11 import glob
12 import os
13 import stat
14 import time
15 import subprocess
16 import threading
17 try:
18         from xml.etree import cElementTree as ElementTree
19 except:
20         from xml.etree import ElementTree
21
22 from Cura.util import profile
23
24 _removableCacheUpdateThread = None
25 _removableCache = []
26
27 def _parseStupidPListXML(e):
28         if e.tag == 'plist':
29                 return _parseStupidPListXML(list(e)[0])
30         if e.tag == 'array':
31                 ret = []
32                 for c in list(e):
33                         ret.append(_parseStupidPListXML(c))
34                 return ret
35         if e.tag == 'dict':
36                 ret = {}
37                 key = None
38                 for c in list(e):
39                         if c.tag == 'key':
40                                 key = c.text
41                         elif key is not None:
42                                 ret[key] = _parseStupidPListXML(c)
43                                 key = None
44                 return ret
45         if e.tag == 'true':
46                 return True
47         if e.tag == 'false':
48                 return False
49         return e.text
50
51 def _findInTree(t, n):
52         ret = []
53         if type(t) is dict:
54                 if '_name' in t and t['_name'] == n:
55                         ret.append(t)
56                 for k, v in t.items():
57                         ret += _findInTree(v, n)
58         if type(t) is list:
59                 for v in t:
60                         ret += _findInTree(v, n)
61         return ret
62
63 def getPossibleSDcardDrives():
64         global _removableCache, _removableCacheUpdateThread
65
66         if profile.getPreference('auto_detect_sd') == 'False':
67                 return []
68
69         if _removableCacheUpdateThread is None:
70                 _removableCacheUpdateThread = threading.Thread(target=_updateCache)
71                 _removableCacheUpdateThread.daemon = True
72                 _removableCacheUpdateThread.start()
73         return _removableCache
74
75 def _updateCache():
76         global _removableCache
77
78         while True:
79                 drives = []
80                 if platform.system() == "Windows":
81                         from ctypes import windll
82                         import ctypes
83                         bitmask = windll.kernel32.GetLogicalDrives()
84                         for letter in string.uppercase:
85                                 if letter != 'A' and letter != 'B' and bitmask & 1 and windll.kernel32.GetDriveTypeA(letter + ':/') == 2:
86                                         volumeName = ''
87                                         nameBuffer = ctypes.create_unicode_buffer(1024)
88                                         if windll.kernel32.GetVolumeInformationW(ctypes.c_wchar_p(letter + ':/'), nameBuffer, ctypes.sizeof(nameBuffer), None, None, None, None, 0) == 0:
89                                                 volumeName = nameBuffer.value
90                                         if volumeName == '':
91                                                 volumeName = 'NO NAME'
92
93                                         freeBytes = ctypes.c_longlong(0)
94                                         if windll.kernel32.GetDiskFreeSpaceExA(letter + ':/', ctypes.byref(freeBytes), None, None) == 0:
95                                                 continue
96                                         if freeBytes.value < 1:
97                                                 continue
98                                         drives.append(('%s (%s:)' % (volumeName, letter), letter + ':/', volumeName))
99                                 bitmask >>= 1
100                 elif platform.system() == "Darwin":
101                         p = subprocess.Popen(['system_profiler', 'SPUSBDataType', '-xml'], stdout=subprocess.PIPE)
102                         xml = ElementTree.fromstring(p.communicate()[0])
103                         p.wait()
104
105                         xml = _parseStupidPListXML(xml)
106                         for dev in _findInTree(xml, 'Mass Storage Device'):
107                                 if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
108                                         for vol in dev['volumes']:
109                                                 if 'mount_point' in vol:
110                                                         volume = vol['mount_point']
111                                                         drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
112
113                         p = subprocess.Popen(['system_profiler', 'SPCardReaderDataType', '-xml'], stdout=subprocess.PIPE)
114                         xml = ElementTree.fromstring(p.communicate()[0])
115                         p.wait()
116
117                         xml = _parseStupidPListXML(xml)
118                         for entry in xml:
119                                 if '_items' in entry:
120                                         for item in entry['_items']:
121                                                 for dev in item['_items']:
122                                                         if 'removable_media' in dev and dev['removable_media'] == 'yes' and 'volumes' in dev and len(dev['volumes']) > 0:
123                                                                 for vol in dev['volumes']:
124                                                                         if 'mount_point' in vol:
125                                                                                 volume = vol['mount_point']
126                                                                                 drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
127                 else:
128                         for volume in glob.glob('/media/*'):
129                                 if os.path.ismount(volume):
130                                         drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
131                                 elif volume == '/media/'+os.getenv('USER'):
132                                         for volume in glob.glob('/media/'+os.getenv('USER')+'/*'):
133                                                 if os.path.ismount(volume):
134                                                         drives.append((os.path.basename(volume), volume + '/', os.path.basename(volume)))
135
136                 _removableCache = drives
137                 time.sleep(1)
138
139 def ejectDrive(driveName):
140         if platform.system() == "Windows":
141                 cmd = [os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'EjectMedia.exe')), driveName]
142         elif platform.system() == "Darwin":
143                 cmd = ["diskutil", "eject", driveName]
144         else:
145                 cmd = ["umount", driveName]
146
147         kwargs = {}
148         if subprocess.mswindows:
149                 su = subprocess.STARTUPINFO()
150                 su.dwFlags |= subprocess.STARTF_USESHOWWINDOW
151                 su.wShowWindow = subprocess.SW_HIDE
152                 kwargs['startupinfo'] = su
153         p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
154         output = p.communicate()
155
156         if p.wait():
157                 print output[0]
158                 print output[1]
159                 return False
160         else:
161                 return True
162
163 if __name__ == '__main__':
164         print getPossibleSDcardDrives()