# Mounting and unmounting volumes in Python

I have a few batch scripts running on an ancient Mac Mini here at home, mostly set up in ~/Library/LaunchAgents with the venerable (but still tremendously useful) Lingon.

A few of them check if other machines are on at the time and rsync files to and fro, partly due to my desire for minimal redundancy and partly because there are some things I want to have on hand (like, say, the last 25 kids’ photos that are usually on my office Mac, but which is not always on).

Most of those are in Python, and despite the native bindings I find it somewhat annoying to fool around with the Scripting Bridge to do basic things, so I often go and invoke osascript directly.

The other day I wanted to mount and unmount network drives, and after googling around a bit for a sane way to handle time outs, I put the following together, which might be of use to someone else as well:

def waitfor(command, timeout):
"""call shell-command and either return its output or kill it
if it doesn't normally exit within timeout seconds and return None"""
import subprocess, datetime, os, time, signal
start = datetime.datetime.now()
process = subprocess.Popen(command, shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while process.poll() is None:
time.sleep(0.2)
now = datetime.datetime.now()
if (now - start).seconds > timeout:
os.kill(process.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
return None
return process.stdout.readlines()

if __name__ == '__main__':
waitfor("""osascript -e 'mount volume "afp://office/photos"'""", 15)
waitfor("""osascript -e 'mount volume "smb://guest:[email protected]/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> *//shared"'""", 15)
sync_last(25, ['/Volumes/photos','/Volumes/shared'])
os.popen("""osascript -e 'tell application "Finder" to eject "Hotwired"'""")
os.popen("""osascript -e 'tell application "Finder" to eject "Shared"'""")


Note the inconsistent invocations of System Events and Finder to mount and unmount the volumes, respectively - that was actually the hardest bit to figure out…

The sync_last function (omitted) grabs the last 25 photos off my office Mini (if it’s on) and copies them - as shrewd readers might guess - onto a pen drive inserted into my Meo router, which happens to have a working (if somewhat funky) DLNA server (the IPTV STB, sadly, doesn’t know the first thing about DLNA in the current release, but I have other things that do).