Skip to content
This repository
tree: 1bc9e89cd1
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 425 lines (388 sloc) 17.677 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
#!/usr/bin/env python

# ;d0KOd. .oOK0x:
# 0xlllcoxolkolllloX
# ;OccloddKNdxdocckc
# .dkddkWNNMOdoxd,
# .o:,x0....xd,ck'
# K:xOoloOOccloocW
# .x;N:....xO....'K;k,
# O..Nc...:XNl...:X.,X
# .kkx0NXk'...dNNxldK'
# 'k...0o....,O...d:
# ;o;'oM0olkWc.;oc
# .cOx....dOl.
# .x00k.

#////////////////////////////
# * Raspberry Pi SD Writer
# * Matt Jump
# * exaviorn.com
#///////////////////////////
# Copyright Matthew Jump 2012
# The following code is licenced under the Gnu Public Licence, please see gpl.txt for reference
# 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 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

# VERSION 1.1 -MACOSX- (May 2012)
# * Summary: Major fixes and renewal of and to the download system, manual system (although it still only works in the same folder as the script).
# An update system has also been included (Please see exaviorn.com for details)
# * For support, please go to raspi.exaviorn.com, or go to our github (https://github.com/exaviorn/RasPiWrite)
# * The script currently only works for Macs, however it can be changed with very little alterations,
# I will add in OS detection logic and the subsequent functions from there

import re, os, urllib2, time, sys, threading
from commands import *
from sys import exit
from random import choice
from xml.dom.minidom import parseString

version = 1.1

#Display Augs
boldStart = "\033[1m"
end = "\033[0;0m"
WARNING = '\033[0;31m'

def checkforUpdate():
print 'Checking for updates...'
global version

try:
file = urllib2.urlopen('http://www.exaviorn.com/raspiwrite.xml', timeout = 1)

data = file.read()
file.close()

dom = parseString(data)

versionToDate = float(dom.getElementsByTagName('Version')[0].toxml().replace('<Version>','').replace('</Version>',''))
summary = dom.getElementsByTagName('Summary')[0].toxml().replace('<Summary>','').replace('</Summary>','')
dlURL = dom.getElementsByTagName('URL')[0].toxml().replace('<URL>','').replace('</URL>','')

if version < versionToDate:
print '#####################################################################################################################'
print 'Your current version (%s) of RasPiWrite is not the latest, please go to the link below to update to version %s,' % (version, versionToDate)
print 'The Changes include: %s' % summary
print '''
Please download the latest version of RasPiWrite from %s''' % dlURL
print '''#####################################################################################################################
'''
else:
print '''Your version of RasPiWrite is up-to-date
'''

except urllib2.URLError, e:
     print """There was an error in checking for an update: %r
""" % e

def grabRoot(distro): #Parses the raspberry pi downloads page for the links for the currently RasPiWrite supported distros
links = list()
htmlSource = urllib2.urlopen('http://www.raspberrypi.org/downloads').read()
linksList = re.findall('href="(.*)"',htmlSource)
for link in linksList:
if distro in link:
if link.endswith('.zip') or link.endswith('.tar.bz2'):
return link


def getZipUrl(url): #gets all the urls that end in .zip or .tar.bz2 (only two disk image archive types on the download web page)
links = list()
htmlSource = urllib2.urlopen(url).read()
linksList = re.findall('<a href="?([^\s^"]+)',htmlSource)
for link in linksList:
if link.endswith('.zip') or link.endswith('.tar.bz2'):
links.append(link)
return links

def findDL(OS): #legacy reasons (Rasberry Pi website doesn't currently list Fedora - http://www.raspberrypi.org/phpBB3/viewtopic.php?f=2&t=5624)
fedora = ['http://achtbaan.nikhef.nl/events/rpi/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://mirror.star.net.uk/raspberrypi/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://www.sqltuning.cz/raspberry/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://raspberrypi.peir.com/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://raspberrypi.mirror.triple-it.nl/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://ftp.ticklers.org/RaspberryPi/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://files.velocix.com/c1410/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz',
'http://raspberrypi.reon.hu/images/fedora/14/r1-06-03-2012/raspberrypi-fedora-remix-14-r1.img.gz']
if OS == 'fedora': return choice(fedora)


def download(url): #http://stackoverflow.com/questions/22676/how-do-i-download-a-file-over-http-using-python | Downloads the disk image for the user
file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)
file_size_dl = 0
block_sz = 8192
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"%10d [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
status = status + chr(8)*(len(status)+1)
print status,
f.close()

def cleanOutput(text, text2): #cleans up the output from df -h
#^(?=.*?\b/\b)(?=.*?\bdisk0)((?!Volume).)*$ <-- regex used
filename = "Example_file_(extra_descriptor).ext"
removeGarbage = re.compile(r'(?=devfs)([^>]*)(?=/dev/)')
removeRootHDD = re.compile(r"(?=.*?\b/\b)(?=.*?\bdisk0)((?!Volume).)*")
cleanOutput = re.sub(removeRootHDD, '', re.sub(removeGarbage, '', text2))
return cleanOutput

def matchSD(input): #grabs just the drive's name from the df -h command (macOSX so far)
selectSD = r"(?=/)(.*?)\s"
match = re.search(selectSD, input)
return match

def unmount(location): #unmounts the drive so that it can be rewrittern
print 'Unmounting the drive in preparation for writing...'
output = getoutput('diskutil unmount ' + location)
print output
if 'Unmount failed for' in output:
print WARNING + 'Error, the Following drive couldn\'t be unmounted, exiting...' + end
exit()

class transferInBackground (threading.Thread): #Runs the dd command in a thread so that I can give a waiting... indicator

   def run ( self ):
global SDsnip
global path
copyString = 'dd bs=1m if=%s of=%s' % (path,SDsnip)
print 'Running ' + copyString + '...'
print getoutput(copyString)
print 'done!'
     

def transfer(file,archiveType,obtain,SD,URL): #unzips the disk image
global path
if archiveType == 'zip':
path = file.replace(".zip", "") + '/' + file.replace(".zip", ".img")
extractCMD = 'unzip ' + file
if archiveType == 'gz':
path = file.replace(".gz", "") #<-- verify
extractCMD = 'gunzip ' + file

if archiveType == 'bz2':
path = '' #probably don't need, but I found during debug that the interpreter would complain about the var not being defined
#QtonPi making me jump through hoops:
basePath = file.replace(".tar.bz2", "")
#this path is actually changed to something that the script can locate, such as the basepath
extractPath = file.replace(".tar.bz2", "") + '/sdcard-img/' + re.search(r"(?=qtonpi)([^>]*)(?=-)", file).group(0) + '-sdcard' + re.search(r"(?=-)([^>]*)(?=.tar)", file).group(0) + '.img.bz2' #<-- verify
finalPath = file.replace(".tar.bz2", "") + '/sdcard-img/' + re.search(r"(?=qtonpi)([^>]*)(?=-)", file).group(0) + '-sdcard' + re.search(r"(?=-)([^>]*)(?=.tar)", file).group(0) + '.img'
extractCMD = 'tar jxf ' + file
path = basePath

if obtain == 'dl':
obtainType = 'Downloaded by this client (reliable and safe)'
if (os.path.exists(path)):
print 'archive already has been extracted, skipping unzipping...'
if archiveType == 'bz2':
path = extractPath
if (os.path.exists(path)):
print 'Unzipping image..'
print getoutput('bunzip2 ' + path)
else:
print 'Image has already been unzipped'
path = finalPath
else:
download(URL)
print 'Ok... Unzipping the disk , this may take a while...'
print getoutput(extractCMD) #extract here!
if archiveType == 'bz2':
path = extractPath
if (os.path.exists(path)):
print 'Unzipping image..'
print getoutput('bunzip2 ' + path)
else:
print 'Image has already been unzipped'
path = finalPath
if obtain == 'usr':
obtainType = 'Obtained by user and passed in (potentially dangerous)'
print 'Found archive inputted by user, extracting...'
if (os.path.exists(path)):
print 'archive already has been extracted, skipping unzipping...'
if archiveType == 'bz2':
path = extractPath
if (os.path.exists(path)):
print 'Unzipping image..'
print getoutput('bunzip2 ' + path)
else:
print 'Image has already been unzipped'
path = finalPath
else:
print 'Ok... Unzipping the disk , this may take a while...'
print getoutput(extractCMD) #extract here!
if archiveType == 'bz2':
path = extractPath
if (os.path.exists(path)):
print 'Unzipping image..'
print getoutput('bunzip2 ' + path)
else:
print 'Image has already been unzipped'
path = finalPath
global SDsnip
SDsnip = SD.replace(' ', '')[:-2]
#print SDsnip <-debug
print path
print '\n\n###################################################################'
print 'About to start the transfer procedure, here is your setup:'
print """
> OS Choice: %s
> SD Card: %s
> Type: %s
""" % (file.replace(".zip", ""), SDsnip, obtainType)
print """
Remember that Matt Jump nor exaviorn.com can be to warranty for any destruction of data or hardware
(excerpt from GNU GPL, which can be found in the script's source, as well as inside the script's folder):
-----------------------------------------------------------------
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-----------------------------------------------------------------
"""
print '###################################################################\n'
confirm = raw_input(boldStart + 'Please verify this information before hitting typing \'accept\', if this information isn\'t correct, please press ctrl + c (^C), or type \'exit\' to quit: ' + end)
if confirm == 'accept':
    thread1 = transferInBackground()
    thread1.start()
sys.stdout.write("Waiting")
    sys.stdout.flush()
while thread1.isAlive():
time.sleep(3)
sys.stdout.write(".")
    sys.stdout.flush()
    print 'Transfer Complete! Please remove the SD card'
    print """###########################################
Relevent information:
> Debian - Login is pi/raspberry
> Arch - Login is root/root
> Fedora - Login is root/fedoraarm
> QtonPi - Login is root/rootme
###########################################
Thank You for using RasPiWrite, you are now free to eject your drive
"""
else:
print 'ENDING WITHOUT COPYING ANY DATA ACROSS, SD CARD HAS BEEN UNMOUNTED'
exit()

def getImage(SD): #gives the user a bunch of options to download an image, or select their own, it then passes the user on to the transfer function
global boldStart
global end
userChoice = raw_input('Do you wish to Download a Raspberry Pi compatible image (choose yes if you don\'t have one) (Y/n): ')
if (userChoice == 'Y') or (userChoice == 'y'):
print boldStart + """
> Debian \"Squeeze\" [OPTION 1]""" + end + """
Reference root filesystem from Gray and Dom, containing LXDE, Midori, development tools
and example source code for multimedia functions.
"""
print boldStart + """
> Arch Linux [OPTION 2]""" + end + """
Arch Linux ARM is based on Arch Linux, which aims for simplicity and full control to the
end user. Note that this distribution may not be suitable for beginners.
"""
print boldStart + """
> Fedora 14 [OPTION 3]""" + end + """
(raspberrypi-fedora-remix-14-r1)
The Raspberry Pi recommended choice for beginners, features a full GUI and applications for
productivity and programming
"""
print boldStart + """
> QtonPi [OPTION 4]""" + end + """
QtonPi is an Embedded Linux platform plus SDK optimized for developing and running Qt 5 Apps on Raspberry Pi.
"""
osChoice = raw_input('Your Choice e.g: \'1\' : ')
if osChoice == '1':
URL = choice(getZipUrl(grabRoot('debian')))
#URL = findDL('debian')
print 'Downloading Debian from [%s]'% URL
match = grabRoot('debian').rpartition('/')
transfer(match[-1],'zip','dl',SD,URL)
if osChoice == '2':
URL = choice(getZipUrl(grabRoot('arch')))
print 'Downloading Arch Linux from [%s]'% URL
match = grabRoot('arch').rpartition('/')
transfer(match[-1],'zip','dl',SD,URL)
if osChoice == '3':
URL = findDL('fedora')
print 'Downloading Fedora 14 from [%s]'% URL
transfer('raspberrypi-fedora-remix-14-r1.img.gz','gz','dl',SD, URL)
if osChoice == '4':
URL = choice(getZipUrl(grabRoot('qtonpi')))
print 'Downloading QtonPi 14 from [%s]'% URL
match = grabRoot('qtonpi').rpartition('/')
transfer(match[-1],'bz2','dl',SD, URL)

if (userChoice == 'N') or (userChoice == 'n'):
userLocate = raw_input('Please locate the disk image (.zip, .img.gz, .tar.bz2 (.tar.bz2 only working with QtonPi distros currently): ')
if (os.path.exists(userLocate)):
matchZip = re.match('^.*\.zip$',userLocate)
matchGzip = re.match('^.*\.img.gz$',userLocate)
matchBzip = re.match('^.*\.tar.bz2$',userLocate)

if matchZip is not None:
print 'Found Zip'
#findDL('debian')
transfer(userLocate,'zip','usr',SD,'none')
if matchGzip is not None:
print 'found Gzip'
transfer(userLocate, 'gz', 'usr',SD,'none')
if matchBzip is not None:
print 'found Bz2'
transfer(userLocate, 'bz2', 'usr',SD,'none')
    

else:
print 'sorry, the file you have specified doesn\'t exist, please try again'
print 'Press ctrl + c (^C) to quit'
exit()

def driveTest(SD):
if (SD == '/dev') or (SD == '/') or (SD == '/home') or (SD == '/net'):
print WARNING + """
#############################################################
WARNING: THE FOLLOWING PREDICTED LOCATION MAY BE INCORRECT!
No reliable SD Card location could be found
#############################################################
""" + end
manualID = raw_input("Please enter the location you believe holds the SD Card: ")
driveTest(manualID)
else:
sdID = raw_input("I believe this is your SD card: " + SD + " is that correct? (Y/n) ")
if (sdID == 'Y') or (sdID == 'y'): #continue
unmount(SD) #<--works, so don't need to test
getImage(SD)

if (sdID == 'N') or (sdID == 'n'):
manualID = raw_input("Please enter the location you believe holds the SD Card: ")
driveTest(manualID)

#logic:
#most of this stuff is pretty self explanitory, some of it could be put into a function, but I don't like
#having loads of micro functions (the ones I do have are going to be expanded to cover all unix based OS's)
print getoutput('clear')
print boldStart + """
;d0KOd. .oOK0x:
0xlllcoxolkolllloX
;OccloddKNdxdocckc
.dkddkWNNMOdoxd,
.o:,x0....xd,ck'
K:xOoloOOccloocW
.x;N:....xO....'K;k,
O..Nc...:XNl...:X.,X
.kkx0NXk'...dNNxldK'
'k...0o....,O...d:
;o;'oM0olkWc.;oc
.cOx....dOl.
.x00k.
""" + end
print """//////////////////////// """ + boldStart + """
* Raspberry Pi SD Writer """ + end + """
* Matt Jump
* exaviorn.com
////////////////////////
(Version 1.1 -MACOSX-)
"""
OS = os.uname() #gets OS vars
if OS[0] != 'Darwin': #if Mac OS, will change to posix once I have worked around some of the command differences
print WARNING + 'I\'m sorry, but your OS isn\'t supported at this time, Linux/Unix users - please tune in soon for a POSIX version' + end
exit()
if not os.geteuid()==0:
print WARNING + 'Please run the script using sudo e.g. sudo python raspiwrite.py, or sudo ./raspiwrite.py (need to chmod +x first)' + end
exit()
checkforUpdate()
print 'The following script is designed to copy a Raspberry Pi compatible disk image to an SD Card'
print boldStart + 'INCORRECTLY FOLLOWING THE WIZARD COULD RESULT IN THE CORRUPTION OF YOUR HARD DISK, PARTITIONS OR A BACKUP USB DRIVE (INCLUDING MOUNTED TIME MACHINE BACKUP DRIVES)' +end
print 'It is advisable to remove any other USB HDDs or memory sticks, the wizard might select that one, %s if you have multiple hard drives installed, please take a LOT of care selecting the right drive %s'% (boldStart, end)
text = getoutput('df -h')
raw_input('Now insert your SD Card, press enter when you are ready...')
text2 = getoutput('df -h')
print """
\n---------------------------------------------------------
""" + boldStart + """The following drives were found:
""" + end
volumes = cleanOutput(text,text2)
print volumes
print '---------------------------------------------------------\n'
SD = matchSD(volumes).group(1) #selects the first SD/USB drive located
driveTest(SD) #action gets delegated to driveTest, which then leads on to the next step, I found this to be the easiest way
Something went wrong with that request. Please try again.