mirror of
https://github.com/munki/munki.git
synced 2026-01-07 06:59:57 -06:00
Add new Sierra-compatible fileshare mounting code to manifestutil.
This commit is contained in:
@@ -32,6 +32,8 @@ import sys
|
||||
import thread
|
||||
import time
|
||||
|
||||
import objc
|
||||
|
||||
from ctypes.util import find_library
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
@@ -43,6 +45,105 @@ except ImportError:
|
||||
'''Placeholder if munkilib is not available'''
|
||||
return 'UNKNOWN'
|
||||
|
||||
# NetFS share mounting code borrowed and liberally adapted from Michael Lynn's
|
||||
# work here: https://gist.github.com/pudquick/1362a8908be01e23041d
|
||||
try:
|
||||
import errno
|
||||
import getpass
|
||||
import objc
|
||||
from CoreFoundation import CFURLCreateWithString
|
||||
|
||||
class Attrdict(dict):
|
||||
'''Custom dict class'''
|
||||
__getattr__ = dict.__getitem__
|
||||
__setattr__ = dict.__setitem__
|
||||
|
||||
NetFS = Attrdict()
|
||||
# Can cheat and provide 'None' for the identifier, it'll just use
|
||||
# frameworkPath instead
|
||||
# scan_classes=False means only add the contents of this Framework
|
||||
NetFS_bundle = objc.initFrameworkWrapper(
|
||||
'NetFS', frameworkIdentifier=None,
|
||||
frameworkPath=objc.pathForFramework('NetFS.framework'),
|
||||
globals=NetFS, scan_classes=False)
|
||||
|
||||
# https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/
|
||||
# ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
|
||||
# Fix NetFSMountURLSync signature
|
||||
del NetFS['NetFSMountURLSync']
|
||||
objc.loadBundleFunctions(
|
||||
NetFS_bundle, NetFS, [('NetFSMountURLSync', 'i@@@@@@o^@')])
|
||||
NETFSMOUNTURLSYNC_AVAILABLE = True
|
||||
except (ImportError, KeyError):
|
||||
NETFSMOUNTURLSYNC_AVAILABLE = False
|
||||
|
||||
if NETFSMOUNTURLSYNC_AVAILABLE:
|
||||
class ShareMountException(Exception):
|
||||
'''An exception raised if share mounting failed'''
|
||||
pass
|
||||
|
||||
|
||||
class ShareAuthenticationNeededException(ShareMountException):
|
||||
'''An exception raised if authentication is needed'''
|
||||
pass
|
||||
|
||||
|
||||
def mount_share(share_url):
|
||||
'''Mounts a share at /Volumes, returns the mount point or raises an
|
||||
error'''
|
||||
sh_url = CFURLCreateWithString(None, share_url, None)
|
||||
# Set UI to reduced interaction
|
||||
open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI}
|
||||
# Allow mounting sub-directories of root shares
|
||||
mount_options = {NetFS.kNetFSAllowSubMountsKey: True}
|
||||
# Mount!
|
||||
result, output = NetFS.NetFSMountURLSync(
|
||||
sh_url, None, None, None, open_options, mount_options, None)
|
||||
# Check if it worked
|
||||
if result != 0:
|
||||
if result in (errno.ENOTSUP, errno.EAUTH):
|
||||
# errno.ENOTSUP is returned if an afp share needs a login
|
||||
# errno.EAUTH is returned if authentication fails (SMB for sure)
|
||||
raise ShareAuthenticationNeededException()
|
||||
raise ShareMountException(
|
||||
'Error mounting url "%s": %s, error %s'
|
||||
% (share_url, os.strerror(result), result))
|
||||
# Return the mountpath
|
||||
return str(output[0])
|
||||
|
||||
|
||||
def mount_share_with_credentials(share_url, username, password):
|
||||
'''Mounts a share at /Volumes, returns the mount point or raises an
|
||||
error. Include username and password as parameters, not in the
|
||||
share_path URL'''
|
||||
sh_url = CFURLCreateWithString(None, share_url, None)
|
||||
# Set UI to reduced interaction
|
||||
open_options = {NetFS.kNAUIOptionKey: NetFS.kNAUIOptionNoUI}
|
||||
# Allow mounting sub-directories of root shares
|
||||
mount_options = {NetFS.kNetFSAllowSubMountsKey: True}
|
||||
# Mount!
|
||||
result, output = NetFS.NetFSMountURLSync(
|
||||
sh_url, None, username, password, open_options, mount_options, None)
|
||||
# Check if it worked
|
||||
if result != 0:
|
||||
raise ShareMountException(
|
||||
'Error mounting url "%s": %s, error %s'
|
||||
% (share_url, os.strerror(result), result))
|
||||
# Return the mountpath
|
||||
return str(output[0])
|
||||
|
||||
|
||||
def mount_share_url(share_url):
|
||||
'''Mount a share url under /Volumes, prompting for password if needed
|
||||
Raises ShareMountException if there's an error'''
|
||||
try:
|
||||
mount_share(share_url)
|
||||
except ShareAuthenticationNeededException:
|
||||
username = raw_input('Username: ')
|
||||
password = getpass.getpass()
|
||||
mount_share_with_credentials(share_url, username, password)
|
||||
|
||||
|
||||
try:
|
||||
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
||||
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
||||
@@ -337,22 +438,32 @@ def mount_repo_cli():
|
||||
repo_url = pref('repo_url')
|
||||
if os.path.exists(repo_path):
|
||||
return
|
||||
os.mkdir(repo_path)
|
||||
print 'Attempting to mount fileshare %s:' % repo_url
|
||||
if repo_url.startswith('afp:'):
|
||||
cmd = ['/sbin/mount_afp', '-i', repo_url, repo_path]
|
||||
elif repo_url.startswith('smb:'):
|
||||
cmd = ['/sbin/mount_smbfs', repo_url[4:], repo_path]
|
||||
elif repo_url.startswith('nfs://'):
|
||||
cmd = ['/sbin/mount_nfs', repo_url[6:], repo_path]
|
||||
print 'Attempting to mount fileshare %s:' % repo_path
|
||||
if NETFSMOUNTURLSYNC_AVAILABLE:
|
||||
# mount the share using the NetFS API
|
||||
try:
|
||||
mount_share_url(repo_url)
|
||||
except ShareMountException, err:
|
||||
print >> sys.stderr, err
|
||||
else:
|
||||
WE_MOUNTED_THE_REPO = True
|
||||
else:
|
||||
print >> sys.stderr, 'Unsupported filesystem URL!'
|
||||
return
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
os.rmdir(repo_path)
|
||||
else:
|
||||
WE_MOUNTED_THE_REPO = True
|
||||
# do it the old way
|
||||
os.mkdir(REPO_PATH)
|
||||
if REPO_URL.startswith('afp:'):
|
||||
cmd = ['/sbin/mount_afp', '-i', repo_url, repo_path]
|
||||
elif REPO_URL.startswith('smb:'):
|
||||
cmd = ['/sbin/mount_smbfs', repo_url[4:], repo_path]
|
||||
elif REPO_URL.startswith('nfs://'):
|
||||
cmd = ['/sbin/mount_nfs', repo_url[6:], repo_path]
|
||||
else:
|
||||
print >> sys.stderr, 'Unsupported filesystem URL!'
|
||||
return
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
os.rmdir(repo_path)
|
||||
else:
|
||||
WE_MOUNTED_THE_REPO = True
|
||||
|
||||
|
||||
def unmount_repo_cli():
|
||||
|
||||
Reference in New Issue
Block a user