mirror of
https://github.com/munki/munki.git
synced 2026-04-30 01:11:01 -05:00
Auth restart support for Managed Software Center.app
This commit is contained in:
Executable
+385
@@ -0,0 +1,385 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2017 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
authrestartd
|
||||
|
||||
Created by Greg Neagle on 2017-04-15.
|
||||
|
||||
Much code based on and adapted from autopkgserver by Per Olofsson
|
||||
|
||||
A helper tool for FileVault authorized restarts. Allows non-privileged users
|
||||
to get certain info about FileVault, and to store a password for authrestart.
|
||||
Root user can trigger an authrestart, possibly using the password stored
|
||||
earlier by a non-privileged user.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import stat
|
||||
import logging
|
||||
import logging.handlers
|
||||
import SocketServer
|
||||
import socket
|
||||
import plistlib
|
||||
import struct
|
||||
|
||||
from munkilib import authrestart
|
||||
from munkilib import launchd
|
||||
from munkilib import prefs
|
||||
|
||||
|
||||
APPNAME = 'authrestartd'
|
||||
VERSION = '0.1'
|
||||
|
||||
|
||||
class FDEUtilError(Exception):
|
||||
'''Exception to raise if there is any error in FDEUtil'''
|
||||
pass
|
||||
|
||||
|
||||
class FDEUtil(object):
|
||||
'''Class for working with fdesetup'''
|
||||
def __init__(self, server, request, uid):
|
||||
'''Arguments:
|
||||
request A request in plist format.
|
||||
uid The uid of the requestor
|
||||
'''
|
||||
self.server = server
|
||||
self.log = server.log
|
||||
self.request = request
|
||||
self.uid = uid
|
||||
|
||||
def verify_request(self):
|
||||
'''Make sure copy request has everything we need'''
|
||||
self.log.debug('Verifying request')
|
||||
for key in ['task']:
|
||||
if not key in self.request:
|
||||
raise FDEUtilError('No %s in request' % key)
|
||||
|
||||
def store_password(self, password):
|
||||
'''Stores a password for later use for authrestart'''
|
||||
self.server.stored_password = password
|
||||
|
||||
def handle(self):
|
||||
'''Handle our request'''
|
||||
self.verify_request()
|
||||
|
||||
if self.request['task'] == 'restart':
|
||||
# Attempt to perform an authrestart, falling back to a regular
|
||||
# restart
|
||||
self.log.info('Restart request from uid %s', self.uid)
|
||||
if self.uid == 0:
|
||||
authrestart.do_authorized_or_normal_restart(
|
||||
password=self.server.stored_password)
|
||||
return 'RESTARTING'
|
||||
else:
|
||||
self.log.info('Restart request denied')
|
||||
raise FDEUtilError('Restart may only be triggered by root')
|
||||
|
||||
elif self.request['task'] == 'store_password':
|
||||
# store a password for later fdesetup authrestart
|
||||
self.log.info('Store password request')
|
||||
username = self.request.get('username')
|
||||
# don't store the password if the user isn't enabled for FileVault
|
||||
if (username and
|
||||
not authrestart.can_attempt_auth_restart_for(username)):
|
||||
self.log.info('User %s can\'t do auth restart', username)
|
||||
raise FDEUtilError(
|
||||
'User %s can\'t do FileVault authrestart' % username)
|
||||
password = self.request.get('password')
|
||||
if not password:
|
||||
self.log.info('No password in request')
|
||||
raise FDEUtilError('No password provided')
|
||||
self.store_password(password)
|
||||
self.log.info('Password stored.')
|
||||
return 'DONE'
|
||||
|
||||
elif self.request['task'] == 'verify_can_attempt_auth_restart':
|
||||
# Check if we have all the required bits to attempt an auth
|
||||
# restart.
|
||||
self.log.info('Verify ready for auth restart')
|
||||
if authrestart.can_attempt_auth_restart(
|
||||
have_password=bool(self.server.stored_password)):
|
||||
self.log.info('Ready for auth restart attempt')
|
||||
return 'READY'
|
||||
else:
|
||||
self.log.info('Not ready for auth restart attempt')
|
||||
raise FDEUtilError('Not ready for auth restart attempt')
|
||||
|
||||
elif self.request['task'] == 'verify_recovery_key_present':
|
||||
# Check if a plist containing a recovery key or password is
|
||||
# present. Return a boolean.
|
||||
self.log.info('Verify recovery key request')
|
||||
if authrestart.get_auth_restart_key(quiet=True) == '':
|
||||
self.log.info('No valid recovery key plist')
|
||||
raise FDEUtilError('No recovery key plist')
|
||||
self.log.info('Valid recovery key plist found')
|
||||
return 'PRESENT'
|
||||
|
||||
elif self.request['task'] == 'verify_user':
|
||||
# Check to see if we can perform an authrestart for this user.
|
||||
# FileVault must be active, the hardware must support authrestart,
|
||||
# and the user must be enabled for FileVault.
|
||||
username = self.request.get('username')
|
||||
if not username:
|
||||
self.log.info('Verify user request with no username')
|
||||
raise FDEUtilError('No username provided')
|
||||
self.log.info('Verify user request for %s', username)
|
||||
if not authrestart.can_attempt_auth_restart_for(username):
|
||||
self.log.info(
|
||||
'User %s can\'t do FileVault authrestart' % username)
|
||||
raise FDEUtilError('User not authorized for FileVault')
|
||||
self.log.info('User %s ok for auth restart', username)
|
||||
return 'USER VERIFIED'
|
||||
|
||||
elif self.request['task'] == 'verify_filevault':
|
||||
# check if FileVault is active
|
||||
self.log.info('Verify FileVault request')
|
||||
if not authrestart.filevault_is_active():
|
||||
self.log.info('FileVault is not active.')
|
||||
raise FDEUtilError('FileVault is not active')
|
||||
self.log.info('FileVault is active.')
|
||||
return 'FILEVAULT ON'
|
||||
|
||||
else:
|
||||
self.log.info('Unknown task request: %s', self.request['task'])
|
||||
raise FDEUtilError('Unknown task')
|
||||
|
||||
|
||||
class RunHandler(SocketServer.StreamRequestHandler):
|
||||
'''Handler for restarthelper run requests'''
|
||||
|
||||
def verify_request_syntax(self, plist):
|
||||
'''Verify the basic syntax of request plist.'''
|
||||
|
||||
# Keep a list of error messages.
|
||||
errors = list()
|
||||
|
||||
# Root should be a dictionary.
|
||||
if not isinstance(plist, dict):
|
||||
errors.append('Request root is not a dictionary')
|
||||
# Bail out early if it's not.
|
||||
return False, errors
|
||||
|
||||
syntax_ok = True
|
||||
# TO-DO: Actual verification!
|
||||
return syntax_ok, errors
|
||||
|
||||
def getpeerid(self):
|
||||
'''
|
||||
Get peer credentials on a UNIX domain socket.
|
||||
Returns uid, gids.
|
||||
'''
|
||||
|
||||
# /usr/include/sys/ucred.h
|
||||
#
|
||||
# struct xucred {
|
||||
# u_int cr_version; /* structure layout version */
|
||||
# uid_t cr_uid; /* effective user id */
|
||||
# short cr_ngroups; /* number of advisory groups */
|
||||
# gid_t cr_groups[NGROUPS]; /* advisory group list */
|
||||
# };
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
LOCAL_PEERCRED = 0x001
|
||||
XUCRED_VERSION = 0
|
||||
NGROUPS = 16
|
||||
# pylint: enable=invalid-name
|
||||
cr_version = 0
|
||||
cr_uid = 1
|
||||
cr_ngroups = 2
|
||||
cr_groups = 3
|
||||
|
||||
xucred_fmt = 'IIh%dI' % NGROUPS
|
||||
res = struct.unpack(
|
||||
xucred_fmt,
|
||||
self.request.getsockopt(
|
||||
0, LOCAL_PEERCRED, struct.calcsize(xucred_fmt)))
|
||||
|
||||
if res[cr_version] != XUCRED_VERSION:
|
||||
raise OSError('Incompatible struct xucred version')
|
||||
|
||||
return res[cr_uid], res[cr_groups:cr_groups + res[cr_ngroups]]
|
||||
|
||||
def handle(self):
|
||||
'''Handle an incoming run request.'''
|
||||
|
||||
try:
|
||||
# Log through server parent.
|
||||
self.log = self.server.log
|
||||
self.log.debug('Handling request')
|
||||
|
||||
# Get uid and primary gid of connecting peer.
|
||||
uid, gids = self.getpeerid()
|
||||
gid = gids[0]
|
||||
self.log.debug(
|
||||
'Got request from uid %d gid %d' % (uid, gid))
|
||||
|
||||
# Receive a plist.
|
||||
plist_string = self.request.recv(8192)
|
||||
|
||||
# Try to parse it.
|
||||
try:
|
||||
plist = plistlib.readPlistFromString(plist_string)
|
||||
except BaseException as err:
|
||||
self.log.error('Malformed request')
|
||||
self.request.send('ERROR:Malformed request\n')
|
||||
return
|
||||
self.log.debug('Parsed request plist')
|
||||
|
||||
# Verify the plist syntax.
|
||||
syntax_ok, errors = self.verify_request_syntax(plist)
|
||||
if not syntax_ok:
|
||||
self.log.error('Plist syntax error')
|
||||
self.request.send(''.join(['ERROR:%s\n' % e for e in errors]))
|
||||
return
|
||||
|
||||
self.log.info(
|
||||
'Dispatching worker to process request for user %d' % (uid))
|
||||
try:
|
||||
fdeutil = FDEUtil(self.server, plist, uid)
|
||||
result = fdeutil.handle()
|
||||
self.request.send(u'OK:%s\n' % result)
|
||||
except FDEUtilError as err:
|
||||
self.request.send(u'ERROR:%s\n' % unicode(err))
|
||||
except BaseException as err:
|
||||
self.log.error('Run failed: %s' % unicode(err))
|
||||
self.request.send(u'ERROR:%s\n' % unicode(err))
|
||||
|
||||
except BaseException as err:
|
||||
self.log.error('Caught exception: %s' % repr(err))
|
||||
self.request.send('ERROR:Caught exception: %s' % repr(err))
|
||||
return
|
||||
|
||||
|
||||
class RestartHelperDaemonError(Exception):
|
||||
'''Exception to raise for RestartHelperDaemon errors'''
|
||||
pass
|
||||
|
||||
|
||||
class RestartHelperDaemon(SocketServer.UnixStreamServer):
|
||||
'''Daemon that runs as root,
|
||||
receiving requests for fdesetup tasks including authrestart.'''
|
||||
|
||||
allow_reuse_address = True
|
||||
request_queue_size = 10
|
||||
timeout = 10
|
||||
|
||||
def __init__(self, socket_fd, RequestHandlerClass):
|
||||
# Avoid initialization of UnixStreamServer as we need to open the
|
||||
# socket from a file descriptor instead of creating our own.
|
||||
self.socket = socket.fromfd(
|
||||
socket_fd, socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.socket.listen(self.request_queue_size)
|
||||
SocketServer.BaseServer.__init__(self,
|
||||
self.socket.getsockname(),
|
||||
RequestHandlerClass)
|
||||
self.timed_out = False
|
||||
self.stored_password = None
|
||||
|
||||
def setup_logging(self):
|
||||
'''Configure logging'''
|
||||
try:
|
||||
self.log = logging.getLogger(APPNAME)
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
log_console = logging.StreamHandler()
|
||||
log_console.setLevel(logging.DEBUG)
|
||||
# store the log in the same directory as ManagedSoftwareUpdate.log
|
||||
logfilepath = os.path.join(
|
||||
os.path.dirname(prefs.pref('LogFile')), APPNAME + '.log')
|
||||
log_file = logging.handlers.RotatingFileHandler(
|
||||
logfilepath, 'a', 100000, 9, 'utf-8')
|
||||
log_file.setLevel(logging.DEBUG)
|
||||
|
||||
console_formatter = logging.Formatter('%(message)s')
|
||||
file_formatter = logging.Formatter(
|
||||
'%(asctime)s %(module)s[%(process)d]: '
|
||||
'%(message)s (%(funcName)s)')
|
||||
|
||||
log_console.setFormatter(console_formatter)
|
||||
log_file.setFormatter(file_formatter)
|
||||
|
||||
self.log.addHandler(log_console)
|
||||
self.log.addHandler(log_file)
|
||||
except (OSError, IOError) as err:
|
||||
raise RestartHelperDaemonError(
|
||||
'Can\'t open log: %s' % (err.strerror))
|
||||
|
||||
def handle_timeout(self):
|
||||
self.timed_out = True
|
||||
|
||||
|
||||
def main(argv):
|
||||
'''Start our daemon, connect to socket and process requests'''
|
||||
# Make sure we're launched as root
|
||||
if os.geteuid() != 0:
|
||||
print >>sys.stderr, '%s must be run as root.' % APPNAME
|
||||
# Sleep to avoid respawn.
|
||||
time.sleep(10)
|
||||
return 1
|
||||
|
||||
# Make sure that the executable and all containing directories are owned
|
||||
# by root:wheel or root:admin, and not writeable by other users.
|
||||
root_uid = 0
|
||||
wheel_gid = 0
|
||||
admin_gid = 80
|
||||
|
||||
exepath = os.path.realpath(os.path.abspath(__file__))
|
||||
path_ok = True
|
||||
while True:
|
||||
info = os.stat(exepath)
|
||||
if info.st_uid != root_uid:
|
||||
print >> sys.stderr, '%s must be owned by root.' % exepath
|
||||
path_ok = False
|
||||
if info.st_gid not in (wheel_gid, admin_gid):
|
||||
print >> sys.stderr, '%s must have group wheel or admin.' % exepath
|
||||
path_ok = False
|
||||
if info.st_mode & stat.S_IWOTH:
|
||||
print >> sys.stderr, '%s mustn\'t be world writeable.' % exepath
|
||||
path_ok = False
|
||||
exepath = os.path.dirname(exepath)
|
||||
if exepath == '/':
|
||||
break
|
||||
|
||||
if not path_ok:
|
||||
# Sleep to avoid immediate respawn.
|
||||
time.sleep(10)
|
||||
return 1
|
||||
|
||||
# Get socket file descriptors from launchd.
|
||||
socket_fd = launchd.get_socket_fd('authrestartd')
|
||||
if not socket_fd:
|
||||
print >> sys.stderr, 'No socket provided to us by launchd'
|
||||
time.sleep(10)
|
||||
return 1
|
||||
|
||||
# Create the daemon object.
|
||||
daemon = RestartHelperDaemon(socket_fd, RunHandler)
|
||||
daemon.setup_logging()
|
||||
|
||||
daemon.log.info('%s v%s starting', APPNAME, VERSION)
|
||||
|
||||
# Once we're launched, we run until restart
|
||||
while True:
|
||||
daemon.handle_request()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -62,7 +62,6 @@ else:
|
||||
from munkilib import info
|
||||
from munkilib import installer
|
||||
from munkilib import installinfo
|
||||
from munkilib import launch
|
||||
from munkilib import munkilog
|
||||
from munkilib import munkistatus
|
||||
from munkilib import osinstaller
|
||||
@@ -74,6 +73,8 @@ else:
|
||||
from munkilib import utils
|
||||
from munkilib import FoundationPlist
|
||||
|
||||
import munkilib.authrestart.client as authrestartd
|
||||
|
||||
|
||||
def signal_handler(signum, dummy_frame):
|
||||
"""Handle any signals we've been told to.
|
||||
@@ -333,7 +334,10 @@ def doRestart():
|
||||
if not consoleuser or consoleuser == u'loginwindow':
|
||||
# no-one is logged in or we're at the loginwindow
|
||||
time.sleep(5)
|
||||
authrestart.do_authorized_or_normal_restart()
|
||||
# try to use authrestartd to do an auth restart; if that fails
|
||||
# do it directly
|
||||
if not authrestartd.restart():
|
||||
authrestart.do_authorized_or_normal_restart()
|
||||
else:
|
||||
if display.munkistatusoutput:
|
||||
# someone is logged in and we're using Managed Software Center.
|
||||
@@ -531,11 +535,6 @@ def warn_if_server_is_default(server):
|
||||
|
||||
def main():
|
||||
"""Main"""
|
||||
if launch.get_socket_fd():
|
||||
munkilog.log('Launched via socket')
|
||||
time.sleep(10)
|
||||
sys.exit()
|
||||
|
||||
# install handler for SIGTERM
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
|
||||
+62
-29
@@ -17,18 +17,18 @@
|
||||
authrestart.py
|
||||
|
||||
Created by Greg Neagle on 2016-12-14.
|
||||
Functions originally written by Wes Whetstone, Summer/Fall 2016
|
||||
Initial work by Wes Whetstone, Summer/Fall 2016
|
||||
|
||||
Functions supporting FileVault authrestart.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
|
||||
from . import display
|
||||
from . import osutils
|
||||
from . import prefs
|
||||
from .. import display
|
||||
from .. import osutils
|
||||
from .. import prefs
|
||||
|
||||
from . import FoundationPlist
|
||||
from .. import FoundationPlist
|
||||
|
||||
|
||||
def filevault_is_active():
|
||||
@@ -77,46 +77,79 @@ def supports_auth_restart():
|
||||
return False
|
||||
|
||||
|
||||
def get_auth_restart_key():
|
||||
def is_fv_user(username):
|
||||
"""Returns a boolean indicating if username is in the list of FileVault
|
||||
authorized users"""
|
||||
cmd = ['/usr/bin/fdesetup', 'list']
|
||||
try:
|
||||
userlist = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
# output is in the format
|
||||
# jsmith,911D2742-7983-436D-9FA3-3F6B7421684B
|
||||
# tstark,5B0EBEE6-0917-47B2-BFF3-78A9DE437D65
|
||||
for line in userlist.splitlines():
|
||||
if line.split(',')[0] == username:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def can_attempt_auth_restart_for(username):
|
||||
'''Returns a boolean to indicate if all the needed conditions are present
|
||||
for us to attempt an authrestart with username's password'''
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
return (os_version_tuple >= (10, 8) and
|
||||
prefs.pref('PerformAuthRestarts') and filevault_is_active() and
|
||||
supports_auth_restart() and is_fv_user(username))
|
||||
|
||||
|
||||
def get_auth_restart_key(quiet=False):
|
||||
"""Returns recovery key as a string... If we failed
|
||||
to get the proper information, returns an empty string"""
|
||||
to get the proper information, returns an empty string.
|
||||
If quiet is set, fail silently"""
|
||||
# checks to see if recovery key preference is set
|
||||
recoverykeyplist = prefs.pref('RecoveryKeyFile')
|
||||
if not recoverykeyplist:
|
||||
display.display_warning(
|
||||
"RecoveryKeyFile preference is not set")
|
||||
if not quiet:
|
||||
display.display_debug1('RecoveryKeyFile preference is not set')
|
||||
return ''
|
||||
display.display_debug1(
|
||||
'RecoveryKeyFile preference is set to %s...', recoverykeyplist)
|
||||
if not quiet:
|
||||
display.display_debug1(
|
||||
'RecoveryKeyFile preference is set to %s...', recoverykeyplist)
|
||||
# try to get the recovery key from the defined location
|
||||
try:
|
||||
keyplist = FoundationPlist.readPlist(recoverykeyplist)
|
||||
recovery_key = keyplist['RecoveryKey'].strip()
|
||||
return recovery_key
|
||||
except FoundationPlist.NSPropertyListSerializationException:
|
||||
display.display_error(
|
||||
'We had trouble getting info from %s...', recoverykeyplist)
|
||||
if not quiet:
|
||||
display.display_error(
|
||||
'We had trouble getting info from %s...', recoverykeyplist)
|
||||
return ''
|
||||
except KeyError:
|
||||
display.display_error(
|
||||
'Problem with key: RecoveryKey in %s...', recoverykeyplist)
|
||||
if not quiet:
|
||||
display.display_error(
|
||||
'Problem with key: RecoveryKey in %s...', recoverykeyplist)
|
||||
return ''
|
||||
|
||||
|
||||
def can_attempt_auth_restart():
|
||||
def can_attempt_auth_restart(have_password=False):
|
||||
'''Returns a boolean to indicate if all the needed conditions are present
|
||||
for us to attempt an authrestart'''
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
return (os_version_tuple >= (10, 8) and
|
||||
prefs.pref('PerformAuthRestarts') and filevault_is_active() and
|
||||
supports_auth_restart() and get_auth_restart_key() != '')
|
||||
prefs.pref('PerformAuthRestarts') and
|
||||
filevault_is_active() and supports_auth_restart() and
|
||||
(get_auth_restart_key(quiet=True) != '' or have_password))
|
||||
|
||||
|
||||
def perform_auth_restart():
|
||||
def perform_auth_restart(password=None):
|
||||
"""When called this will perform an authorized restart. Before trying
|
||||
to perform an authorized restart it checks to see if the machine supports
|
||||
the feature. If supported it will then look for the defined plist containing
|
||||
a key called RecoveryKey. It will use that value to perform the restart"""
|
||||
the feature. If supported it will look for the defined plist containing
|
||||
a key called RecoveryKey. If this doesn't exist, it will use a password
|
||||
(or recovery key) passed into the function. It will use that value to
|
||||
perform the restart."""
|
||||
display.display_debug1(
|
||||
'Checking if performing an Auth Restart is fully supported...')
|
||||
if not supports_auth_restart():
|
||||
@@ -124,18 +157,18 @@ def perform_auth_restart():
|
||||
"Machine doesn't support Authorized Restarts...")
|
||||
return False
|
||||
display.display_debug1('Machine supports Authorized Restarts...')
|
||||
recovery_key = get_auth_restart_key()
|
||||
recovery_key = get_auth_restart_key() or password
|
||||
if not recovery_key:
|
||||
return False
|
||||
key = {'Password': recovery_key}
|
||||
inputplist = FoundationPlist.writePlistToString(key)
|
||||
display.display_info('Attempting an Authorized Restart now...')
|
||||
cmd = subprocess.Popen(
|
||||
proc = subprocess.Popen(
|
||||
['/usr/bin/fdesetup', 'authrestart', '-inputplist'],
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(dummy_out, err) = cmd.communicate(input=inputplist)
|
||||
err = proc.communicate(input=inputplist)[1]
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version_tuple >= (10, 12) and 'System is being restarted' in err:
|
||||
return True
|
||||
@@ -146,17 +179,17 @@ def perform_auth_restart():
|
||||
return True
|
||||
|
||||
|
||||
def do_authorized_or_normal_restart():
|
||||
def do_authorized_or_normal_restart(password=None):
|
||||
'''Do an authrestart if allowed/possible, else do a normal restart.'''
|
||||
display.display_info('Restarting now.')
|
||||
os_version_tuple = osutils.getOsVersion(as_tuple=True)
|
||||
if (prefs.pref('PerformAuthRestarts')
|
||||
and prefs.pref('RecoveryKeyFile')
|
||||
and os_version_tuple >= (10, 8)):
|
||||
if (prefs.pref('PerformAuthRestarts') and
|
||||
(prefs.pref('RecoveryKeyFile') or password) and
|
||||
os_version_tuple >= (10, 8)):
|
||||
if filevault_is_active():
|
||||
display.display_debug1('Configured to perform AuthRestarts...')
|
||||
# try to perform an auth restart
|
||||
if not perform_auth_restart():
|
||||
if not perform_auth_restart(password=password):
|
||||
# if we got to here then the auth restart failed
|
||||
# notify that it did then perform a normal restart
|
||||
display.display_warning(
|
||||
@@ -0,0 +1,200 @@
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2017 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
authrestart.client.py
|
||||
|
||||
Created by Greg Neagle on 2017-04-15.
|
||||
|
||||
Routines for communicating with authrestartd.
|
||||
Socket communications code adapted from autopkg's PkgCreator by Per Olofsson
|
||||
"""
|
||||
|
||||
import os
|
||||
import plistlib
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
|
||||
|
||||
AUTHRESTARTD_SOCKET = "/var/run/authrestartd"
|
||||
|
||||
|
||||
class AuthRestartClientError(Exception):
|
||||
'''Exception to raise for errors in AuthRestartClient'''
|
||||
pass
|
||||
|
||||
|
||||
class AuthRestartClient(object):
|
||||
'''Handles communication with authrestartd daemon'''
|
||||
def connect(self):
|
||||
'''Connect to authrestartd'''
|
||||
try:
|
||||
#pylint: disable=attribute-defined-outside-init
|
||||
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
#pylint: enable=attribute-defined-outside-init
|
||||
self.socket.connect(AUTHRESTARTD_SOCKET)
|
||||
except socket.error as err:
|
||||
raise AuthRestartClientError(
|
||||
"Couldn't connect to authrestartd: %s" % err.strerror)
|
||||
|
||||
def send_request(self, request):
|
||||
'''Send a request to authrestartd'''
|
||||
self.socket.send(plistlib.writePlistToString(request))
|
||||
with os.fdopen(self.socket.fileno()) as fileref:
|
||||
# use select so we don't hang indefinitely if authrestartd dies
|
||||
ready = select.select([fileref], [], [], 2)
|
||||
if ready[0]:
|
||||
reply = fileref.read()
|
||||
else:
|
||||
reply = ''
|
||||
|
||||
if reply:
|
||||
return reply.rstrip()
|
||||
else:
|
||||
return "ERROR:No reply"
|
||||
|
||||
def disconnect(self):
|
||||
'''Disconnect from authrestartd'''
|
||||
self.socket.close()
|
||||
|
||||
def process(self, request):
|
||||
'''Send a request and return the result'''
|
||||
try:
|
||||
self.connect()
|
||||
result = self.send_request(request)
|
||||
finally:
|
||||
self.disconnect()
|
||||
return result
|
||||
|
||||
def fv_is_active(self):
|
||||
'''Returns a boolean to indicate if FileVault is active'''
|
||||
result = self.process({'task': 'verify_filevault'})
|
||||
return result.startswith('OK')
|
||||
|
||||
def verify_user(self, username):
|
||||
'''Returns True if username can unlock the FV volume'''
|
||||
request = {'task': 'verify_user', 'username': username}
|
||||
result = self.process(request)
|
||||
return result.startswith('OK')
|
||||
|
||||
def verify_recovery_key_present(self):
|
||||
'''Returns True if plist containing a FV recovery key is present'''
|
||||
request = {'task': 'verify_recovery_key_present'}
|
||||
result = self.process(request)
|
||||
return result.startswith('OK')
|
||||
|
||||
def verify_can_attempt_auth_restart(self):
|
||||
'''Returns True if we are ready to attempt an auth restart'''
|
||||
request = {'task': 'verify_can_attempt_auth_restart'}
|
||||
result = self.process(request)
|
||||
return result.startswith('OK')
|
||||
|
||||
def store_password(self, password, username=None):
|
||||
'''Stores a FV password with authrestartd'''
|
||||
request = {'task': 'store_password', 'password': password}
|
||||
if username:
|
||||
request['username'] = username
|
||||
result = self.process(request)
|
||||
if not result.startswith('OK'):
|
||||
raise AuthRestartClientError(result)
|
||||
|
||||
def restart(self):
|
||||
'''Returns True if restart was successful'''
|
||||
result = self.process({'task': 'restart'})
|
||||
if not result.startswith('OK'):
|
||||
raise AuthRestartClientError(result)
|
||||
|
||||
|
||||
# Higher-level wrapper functions that swallow AuthRestartClientErrors
|
||||
def fv_is_active():
|
||||
'''Returns True if FileVault can be verified to be active,
|
||||
False otherwise'''
|
||||
try:
|
||||
return AuthRestartClient().fv_is_active()
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def verify_user(username):
|
||||
'''Returns True if user can be verified to be able to perform an
|
||||
authrestart, False otherwise'''
|
||||
try:
|
||||
return AuthRestartClient().verify_user(username)
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def verify_recovery_key_present():
|
||||
'''Returns True if we have a plist with a FileVault recovery key,
|
||||
False otherwise'''
|
||||
try:
|
||||
return AuthRestartClient().verify_recovery_key_present()
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def verify_can_attempt_auth_restart():
|
||||
'''Returns True if we have what we need to attempt an auth restart'''
|
||||
try:
|
||||
return AuthRestartClient().verify_can_attempt_auth_restart()
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def store_password(password, username=None):
|
||||
'''Stores a password for later authrestart usage.
|
||||
Returns boolean to indicate success/failure'''
|
||||
try:
|
||||
AuthRestartClient().store_password(password, username=username)
|
||||
return True
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def restart():
|
||||
'''Performs a restart -- authenticated if possible.
|
||||
Returns boolean to indicate success/failure'''
|
||||
try:
|
||||
AuthRestartClient().restart()
|
||||
return True
|
||||
except AuthRestartClientError:
|
||||
return False
|
||||
|
||||
|
||||
def test():
|
||||
import getpass
|
||||
import pwd
|
||||
|
||||
print 'FileVault is active: %s' % fv_is_active()
|
||||
print 'Recovery key is present: %s' % verify_recovery_key_present()
|
||||
username = pwd.getpwuid(os.getuid()).pw_name
|
||||
print '%s is FV user: %s' % (username, verify_user(username))
|
||||
password = getpass.getpass('Enter password: ')
|
||||
if password:
|
||||
if username == 'root':
|
||||
username = None
|
||||
if store_password(password, username=username):
|
||||
print 'store_password was successful'
|
||||
else:
|
||||
print 'store_password failed'
|
||||
print 'Can attempt auth restart: %s' % verify_can_attempt_auth_restart()
|
||||
answer = raw_input('Test auth restart (y/n)? ')
|
||||
if answer.lower().startswith('y'):
|
||||
print 'Attempting auth restart...'
|
||||
if restart():
|
||||
print 'restart was successfully triggered'
|
||||
else:
|
||||
print 'restart failed'
|
||||
@@ -1,56 +0,0 @@
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2017 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
launch.__init__.py
|
||||
|
||||
Created by Greg Neagle on 2017-04-14.
|
||||
|
||||
Code for getting socket file descriptor from launchd.
|
||||
Returns a file descriptor for our socket.
|
||||
"""
|
||||
|
||||
from .. import osutils
|
||||
|
||||
SOCKET_NAME = 'managedsoftwareupdated'
|
||||
|
||||
|
||||
def get_socket_fd():
|
||||
'''Get socket file descriptors from launchd.'''
|
||||
os_version = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version >= (10, 10):
|
||||
# use new launchd api
|
||||
from . import launch2
|
||||
try:
|
||||
sockets = launch2.launch_activate_socket(SOCKET_NAME)
|
||||
except launch2.LaunchDError:
|
||||
# no sockets found
|
||||
return None
|
||||
return sockets[0]
|
||||
|
||||
else:
|
||||
# use old launchd api
|
||||
from . import launch1
|
||||
try:
|
||||
socket_dict = launch1.get_launchd_socket_fds()
|
||||
except launch1.LaunchDCheckInError:
|
||||
# no sockets found
|
||||
return None
|
||||
|
||||
if SOCKET_NAME not in socket_dict:
|
||||
# no sockets found with the expected name
|
||||
return None
|
||||
|
||||
return socket_dict[SOCKET_NAME][0]
|
||||
@@ -1,254 +0,0 @@
|
||||
#
|
||||
# Copyright 2010 Per Olofsson
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from ctypes import *
|
||||
libc = CDLL("/usr/lib/libc.dylib")
|
||||
|
||||
|
||||
c_launch_data_t = c_void_p
|
||||
|
||||
# size_t launch_data_array_get_count(const launch_data_t)
|
||||
launch_data_array_get_count = libc.launch_data_array_get_count
|
||||
launch_data_array_get_count.restype = c_size_t
|
||||
launch_data_array_get_count.argtypes = [c_launch_data_t]
|
||||
|
||||
#launch_data_t launch_data_array_get_index(const launch_data_t, size_t) __ld_getter;
|
||||
launch_data_array_get_index = libc.launch_data_array_get_index
|
||||
launch_data_array_get_index.restype = c_launch_data_t
|
||||
launch_data_array_get_index.argtypes = [c_launch_data_t, c_size_t]
|
||||
|
||||
# size_t launch_data_dict_get_count(const launch_data_t)
|
||||
launch_data_dict_get_count = libc.launch_data_dict_get_count
|
||||
launch_data_dict_get_count.restype = c_size_t
|
||||
launch_data_dict_get_count.argtypes = [c_launch_data_t]
|
||||
|
||||
# launch_data_t launch_data_dict_lookup(const launch_data_t, const char *)
|
||||
launch_data_dict_lookup = libc.launch_data_dict_lookup
|
||||
launch_data_dict_lookup.restype = c_launch_data_t
|
||||
launch_data_dict_lookup.argtypes = [c_launch_data_t, c_char_p]
|
||||
|
||||
#void launch_data_dict_iterate(const launch_data_t, void (*)(const launch_data_t, const char *, void *), void *) __ld_iterator(1, 2)
|
||||
DICTITCALLBACK = CFUNCTYPE(c_void_p, c_launch_data_t, c_char_p, c_void_p)
|
||||
launch_data_dict_iterate = libc.launch_data_dict_iterate
|
||||
launch_data_dict_iterate.restype = None
|
||||
launch_data_dict_iterate.argtypes = [c_launch_data_t, DICTITCALLBACK, c_void_p]
|
||||
|
||||
# void launch_data_free(launch_data_t)
|
||||
launch_data_free = libc.launch_data_free
|
||||
launch_data_free.restype = None
|
||||
launch_data_free.argtypes = [c_launch_data_t]
|
||||
|
||||
# int launch_data_get_errno(const launch_data_t)
|
||||
launch_data_get_errno = libc.launch_data_get_errno
|
||||
launch_data_get_errno.restype = c_int
|
||||
launch_data_get_errno.argtypes = [c_launch_data_t]
|
||||
|
||||
# int launch_data_get_fd(const launch_data_t)
|
||||
launch_data_get_fd = libc.launch_data_get_fd
|
||||
launch_data_get_fd.restype = c_int
|
||||
launch_data_get_fd.argtypes = [c_launch_data_t]
|
||||
|
||||
# launch_data_type_t launch_data_get_type(const launch_data_t)
|
||||
launch_data_get_type = libc.launch_data_get_type
|
||||
launch_data_get_type.restype = c_launch_data_t
|
||||
launch_data_get_type.argtypes = [c_launch_data_t]
|
||||
|
||||
# launch_data_t launch_data_new_string(const char *)
|
||||
launch_data_new_string = libc.launch_data_new_string
|
||||
launch_data_new_string.restype = c_launch_data_t
|
||||
launch_data_new_string.argtypes = [c_char_p]
|
||||
|
||||
# launch_data_t launch_msg(const launch_data_t)
|
||||
launch_msg = libc.launch_msg
|
||||
launch_msg.restype = c_launch_data_t
|
||||
launch_msg.argtypes = [c_launch_data_t]
|
||||
|
||||
|
||||
LAUNCH_KEY_SUBMITJOB = c_char_p("SubmitJob")
|
||||
LAUNCH_KEY_REMOVEJOB = c_char_p("RemoveJob")
|
||||
LAUNCH_KEY_STARTJOB = c_char_p("StartJob")
|
||||
LAUNCH_KEY_STOPJOB = c_char_p("StopJob")
|
||||
LAUNCH_KEY_GETJOB = c_char_p("GetJob")
|
||||
LAUNCH_KEY_GETJOBS = c_char_p("GetJobs")
|
||||
LAUNCH_KEY_CHECKIN = c_char_p("CheckIn")
|
||||
|
||||
LAUNCH_JOBKEY_LABEL = c_char_p("Label")
|
||||
LAUNCH_JOBKEY_DISABLED = c_char_p("Disabled")
|
||||
LAUNCH_JOBKEY_USERNAME = c_char_p("UserName")
|
||||
LAUNCH_JOBKEY_GROUPNAME = c_char_p("GroupName")
|
||||
LAUNCH_JOBKEY_TIMEOUT = c_char_p("TimeOut")
|
||||
LAUNCH_JOBKEY_EXITTIMEOUT = c_char_p("ExitTimeOut")
|
||||
LAUNCH_JOBKEY_INITGROUPS = c_char_p("InitGroups")
|
||||
LAUNCH_JOBKEY_SOCKETS = c_char_p("Sockets")
|
||||
LAUNCH_JOBKEY_MACHSERVICES = c_char_p("MachServices")
|
||||
LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES = c_char_p("MachServiceLookupPolicies")
|
||||
LAUNCH_JOBKEY_INETDCOMPATIBILITY = c_char_p("inetdCompatibility")
|
||||
LAUNCH_JOBKEY_ENABLEGLOBBING = c_char_p("EnableGlobbing")
|
||||
LAUNCH_JOBKEY_PROGRAMARGUMENTS = c_char_p("ProgramArguments")
|
||||
LAUNCH_JOBKEY_PROGRAM = c_char_p("Program")
|
||||
LAUNCH_JOBKEY_ONDEMAND = c_char_p("OnDemand")
|
||||
LAUNCH_JOBKEY_KEEPALIVE = c_char_p("KeepAlive")
|
||||
LAUNCH_JOBKEY_LIMITLOADTOHOSTS = c_char_p("LimitLoadToHosts")
|
||||
LAUNCH_JOBKEY_LIMITLOADFROMHOSTS = c_char_p("LimitLoadFromHosts")
|
||||
LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE = c_char_p("LimitLoadToSessionType")
|
||||
LAUNCH_JOBKEY_RUNATLOAD = c_char_p("RunAtLoad")
|
||||
LAUNCH_JOBKEY_ROOTDIRECTORY = c_char_p("RootDirectory")
|
||||
LAUNCH_JOBKEY_WORKINGDIRECTORY = c_char_p("WorkingDirectory")
|
||||
LAUNCH_JOBKEY_ENVIRONMENTVARIABLES = c_char_p("EnvironmentVariables")
|
||||
LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES = c_char_p("UserEnvironmentVariables")
|
||||
LAUNCH_JOBKEY_UMASK = c_char_p("Umask")
|
||||
LAUNCH_JOBKEY_NICE = c_char_p("Nice")
|
||||
LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST = c_char_p("HopefullyExitsFirst")
|
||||
LAUNCH_JOBKEY_HOPEFULLYEXITSLAST = c_char_p("HopefullyExitsLast")
|
||||
LAUNCH_JOBKEY_LOWPRIORITYIO = c_char_p("LowPriorityIO")
|
||||
LAUNCH_JOBKEY_SESSIONCREATE = c_char_p("SessionCreate")
|
||||
LAUNCH_JOBKEY_STARTONMOUNT = c_char_p("StartOnMount")
|
||||
LAUNCH_JOBKEY_SOFTRESOURCELIMITS = c_char_p("SoftResourceLimits")
|
||||
LAUNCH_JOBKEY_HARDRESOURCELIMITS = c_char_p("HardResourceLimits")
|
||||
LAUNCH_JOBKEY_STANDARDINPATH = c_char_p("StandardInPath")
|
||||
LAUNCH_JOBKEY_STANDARDOUTPATH = c_char_p("StandardOutPath")
|
||||
LAUNCH_JOBKEY_STANDARDERRORPATH = c_char_p("StandardErrorPath")
|
||||
LAUNCH_JOBKEY_DEBUG = c_char_p("Debug")
|
||||
LAUNCH_JOBKEY_WAITFORDEBUGGER = c_char_p("WaitForDebugger")
|
||||
LAUNCH_JOBKEY_QUEUEDIRECTORIES = c_char_p("QueueDirectories")
|
||||
LAUNCH_JOBKEY_WATCHPATHS = c_char_p("WatchPaths")
|
||||
LAUNCH_JOBKEY_STARTINTERVAL = c_char_p("StartInterval")
|
||||
LAUNCH_JOBKEY_STARTCALENDARINTERVAL = c_char_p("StartCalendarInterval")
|
||||
LAUNCH_JOBKEY_BONJOURFDS = c_char_p("BonjourFDs")
|
||||
LAUNCH_JOBKEY_LASTEXITSTATUS = c_char_p("LastExitStatus")
|
||||
LAUNCH_JOBKEY_PID = c_char_p("PID")
|
||||
LAUNCH_JOBKEY_THROTTLEINTERVAL = c_char_p("ThrottleInterval")
|
||||
LAUNCH_JOBKEY_LAUNCHONLYONCE = c_char_p("LaunchOnlyOnce")
|
||||
LAUNCH_JOBKEY_ABANDONPROCESSGROUP = c_char_p("AbandonProcessGroup")
|
||||
LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN = c_char_p("IgnoreProcessGroupAtShutdown")
|
||||
LAUNCH_JOBKEY_POLICIES = c_char_p("Policies")
|
||||
LAUNCH_JOBKEY_ENABLETRANSACTIONS = c_char_p("EnableTransactions")
|
||||
|
||||
LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS = c_char_p("DenyCreatingOtherJobs")
|
||||
|
||||
LAUNCH_JOBINETDCOMPATIBILITY_WAIT = c_char_p("Wait")
|
||||
|
||||
LAUNCH_JOBKEY_MACH_RESETATCLOSE = c_char_p("ResetAtClose")
|
||||
LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN = c_char_p("HideUntilCheckIn")
|
||||
LAUNCH_JOBKEY_MACH_DRAINMESSAGESONCRASH = c_char_p("DrainMessagesOnCrash")
|
||||
|
||||
LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT = c_char_p("SuccessfulExit")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE = c_char_p("NetworkState")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE = c_char_p("PathState")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE = c_char_p("OtherJobActive")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED = c_char_p("OtherJobEnabled")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND = c_char_p("AfterInitialDemand")
|
||||
|
||||
LAUNCH_JOBKEY_CAL_MINUTE = c_char_p("Minute")
|
||||
LAUNCH_JOBKEY_CAL_HOUR = c_char_p("Hour")
|
||||
LAUNCH_JOBKEY_CAL_DAY = c_char_p("Day")
|
||||
LAUNCH_JOBKEY_CAL_WEEKDAY = c_char_p("Weekday")
|
||||
LAUNCH_JOBKEY_CAL_MONTH = c_char_p("Month")
|
||||
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_CORE = c_char_p("Core")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_CPU = c_char_p("CPU")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_DATA = c_char_p("Data")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE = c_char_p("FileSize")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK = c_char_p("MemoryLock")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE = c_char_p("NumberOfFiles")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_NPROC = c_char_p("NumberOfProcesses")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_RSS = c_char_p("ResidentSetSize")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_STACK = c_char_p("Stack")
|
||||
|
||||
LAUNCH_JOBKEY_DISABLED_MACHINETYPE = c_char_p("MachineType")
|
||||
LAUNCH_JOBKEY_DISABLED_MODELNAME = c_char_p("ModelName")
|
||||
|
||||
LAUNCH_JOBSOCKETKEY_TYPE = c_char_p("SockType")
|
||||
LAUNCH_JOBSOCKETKEY_PASSIVE = c_char_p("SockPassive")
|
||||
LAUNCH_JOBSOCKETKEY_BONJOUR = c_char_p("Bonjour")
|
||||
LAUNCH_JOBSOCKETKEY_SECUREWITHKEY = c_char_p("SecureSocketWithKey")
|
||||
LAUNCH_JOBSOCKETKEY_PATHNAME = c_char_p("SockPathName")
|
||||
LAUNCH_JOBSOCKETKEY_PATHMODE = c_char_p("SockPathMode")
|
||||
LAUNCH_JOBSOCKETKEY_NODENAME = c_char_p("SockNodeName")
|
||||
LAUNCH_JOBSOCKETKEY_SERVICENAME = c_char_p("SockServiceName")
|
||||
LAUNCH_JOBSOCKETKEY_FAMILY = c_char_p("SockFamily")
|
||||
LAUNCH_JOBSOCKETKEY_PROTOCOL = c_char_p("SockProtocol")
|
||||
LAUNCH_JOBSOCKETKEY_MULTICASTGROUP = c_char_p("MulticastGroup")
|
||||
|
||||
|
||||
(
|
||||
LAUNCH_DATA_DICTIONARY,
|
||||
LAUNCH_DATA_ARRAY,
|
||||
LAUNCH_DATA_FD,
|
||||
LAUNCH_DATA_INTEGER,
|
||||
LAUNCH_DATA_REAL,
|
||||
LAUNCH_DATA_BOOL,
|
||||
LAUNCH_DATA_STRING,
|
||||
LAUNCH_DATA_OPAQUE,
|
||||
LAUNCH_DATA_ERRNO,
|
||||
LAUNCH_DATA_MACHPORT
|
||||
) = range(1, 11)
|
||||
|
||||
|
||||
class LaunchDCheckInError(Exception):
|
||||
pass
|
||||
|
||||
def get_launchd_socket_fds():
|
||||
"""Check in with launchd to get socket file descriptors."""
|
||||
|
||||
# Return a dictionary with keys pointing to lists of file descriptors.
|
||||
launchd_socket_fds = dict()
|
||||
|
||||
# Callback for dict iterator.
|
||||
def add_socket(launch_array, name, context=None):
|
||||
if launch_data_get_type(launch_array) != LAUNCH_DATA_ARRAY:
|
||||
raise LaunchDCheckInError("Could not get file descriptor array: Type mismatch")
|
||||
fds = list()
|
||||
for i in range(launch_data_array_get_count(launch_array)):
|
||||
data_fd = launch_data_array_get_index(launch_array, i)
|
||||
if launch_data_get_type(data_fd) != LAUNCH_DATA_FD:
|
||||
raise LaunchDCheckInError("Could not get file descriptor array entry: Type mismatch")
|
||||
fds.append(launch_data_get_fd(data_fd))
|
||||
launchd_socket_fds[name] = fds
|
||||
|
||||
# Wrap in try/finally to free resources allocated during lookup.
|
||||
try:
|
||||
# Create a checkin request.
|
||||
checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN);
|
||||
if checkin_request == None:
|
||||
raise LaunchDCheckInError("Could not create checkin request")
|
||||
|
||||
# Check the checkin response.
|
||||
checkin_response = launch_msg(checkin_request);
|
||||
if checkin_response == None:
|
||||
raise LaunchDCheckInError("Error checking in")
|
||||
|
||||
if launch_data_get_type(checkin_response) == LAUNCH_DATA_ERRNO:
|
||||
errno = launch_data_get_errno(checkin_response)
|
||||
raise LaunchDCheckInError("Checkin failed")
|
||||
|
||||
# Get a dictionary of sockets.
|
||||
sockets = launch_data_dict_lookup(checkin_response, LAUNCH_JOBKEY_SOCKETS);
|
||||
if sockets == None:
|
||||
raise LaunchDCheckInError("Could not get socket dictionary from checkin response")
|
||||
|
||||
if launch_data_get_type(sockets) != LAUNCH_DATA_DICTIONARY:
|
||||
raise LaunchDCheckInError("Could not get socket dictionary from checkin response: Type mismatch")
|
||||
|
||||
# Iterate over the items with add_socket callback.
|
||||
launch_data_dict_iterate(sockets, DICTITCALLBACK(add_socket), None)
|
||||
|
||||
return launchd_socket_fds
|
||||
|
||||
finally:
|
||||
if checkin_response is not None:
|
||||
launch_data_free(checkin_response)
|
||||
if checkin_request is not None:
|
||||
launch_data_free(checkin_request)
|
||||
|
||||
@@ -2,23 +2,27 @@
|
||||
#
|
||||
# Copyright 2011-2017 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
launchd.py
|
||||
launchd
|
||||
|
||||
Created by Greg Neagle on 2011-07-22.
|
||||
get_socket_fd and refactoring by Greg Neagle on 2017-04-14.
|
||||
|
||||
A wrapper for using launchd to run a process as root outside of munki's
|
||||
Code for getting a socket file descriptor from launchd.
|
||||
Returns a file descriptor for a socket defined in a launchd plist.
|
||||
-and-
|
||||
A wrapper for using launchd to run a process as root outside of Munki's
|
||||
process space. Needed to properly run /usr/sbin/softwareupdate, for example.
|
||||
"""
|
||||
|
||||
@@ -27,8 +31,37 @@ import subprocess
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from . import osutils
|
||||
from . import FoundationPlist
|
||||
from .. import osutils
|
||||
from .. import FoundationPlist
|
||||
|
||||
|
||||
def get_socket_fd(socket_name):
|
||||
'''Get socket file descriptors from launchd.'''
|
||||
os_version = osutils.getOsVersion(as_tuple=True)
|
||||
if os_version >= (10, 10):
|
||||
# use new launchd api
|
||||
from . import launch2
|
||||
try:
|
||||
sockets = launch2.launch_activate_socket(socket_name)
|
||||
except launch2.LaunchDError:
|
||||
# no sockets found
|
||||
return None
|
||||
return sockets[0]
|
||||
|
||||
else:
|
||||
# use old launchd api
|
||||
from . import launch1
|
||||
try:
|
||||
socket_dict = launch1.get_launchd_socket_fds()
|
||||
except launch1.LaunchDCheckInError:
|
||||
# no sockets found
|
||||
return None
|
||||
|
||||
if socket_name not in socket_dict:
|
||||
# no sockets found with the expected name
|
||||
return None
|
||||
|
||||
return socket_dict[socket_name][0]
|
||||
|
||||
|
||||
class LaunchdJobException(Exception):
|
||||
@@ -177,4 +210,4 @@ class Job(object):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print 'This is a library of support tools for the Munki Suite.'
|
||||
print 'This is a library of support tools for the Munki Suite.'
|
||||
Executable
+271
@@ -0,0 +1,271 @@
|
||||
#
|
||||
# Copyright 2010 Per Olofsson
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
'''Python wrapper for original launchd checkin API'''
|
||||
|
||||
# pylint: disable=wildcard-import
|
||||
# pylint: disable=unused-wildcard-import
|
||||
from ctypes import *
|
||||
# pylint: enable=unused-wildcard-import
|
||||
# pylint: enable=wildcard-import
|
||||
# pylint: disable=invalid-name
|
||||
libc = CDLL("/usr/lib/libc.dylib")
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
c_launch_data_t = c_void_p
|
||||
|
||||
# size_t launch_data_array_get_count(const launch_data_t)
|
||||
launch_data_array_get_count = libc.launch_data_array_get_count
|
||||
launch_data_array_get_count.restype = c_size_t
|
||||
launch_data_array_get_count.argtypes = [c_launch_data_t]
|
||||
|
||||
#launch_data_t launch_data_array_get_index(const launch_data_t, size_t)
|
||||
# __ld_getter;
|
||||
launch_data_array_get_index = libc.launch_data_array_get_index
|
||||
launch_data_array_get_index.restype = c_launch_data_t
|
||||
launch_data_array_get_index.argtypes = [c_launch_data_t, c_size_t]
|
||||
|
||||
# size_t launch_data_dict_get_count(const launch_data_t)
|
||||
launch_data_dict_get_count = libc.launch_data_dict_get_count
|
||||
launch_data_dict_get_count.restype = c_size_t
|
||||
launch_data_dict_get_count.argtypes = [c_launch_data_t]
|
||||
|
||||
# launch_data_t launch_data_dict_lookup(const launch_data_t, const char *)
|
||||
launch_data_dict_lookup = libc.launch_data_dict_lookup
|
||||
launch_data_dict_lookup.restype = c_launch_data_t
|
||||
launch_data_dict_lookup.argtypes = [c_launch_data_t, c_char_p]
|
||||
|
||||
#void launch_data_dict_iterate(const launch_data_t, void (*)
|
||||
# (const launch_data_t, const char *, void *), void *) __ld_iterator(1, 2)
|
||||
DICTITCALLBACK = CFUNCTYPE(c_void_p, c_launch_data_t, c_char_p, c_void_p)
|
||||
launch_data_dict_iterate = libc.launch_data_dict_iterate
|
||||
launch_data_dict_iterate.restype = None
|
||||
launch_data_dict_iterate.argtypes = [c_launch_data_t, DICTITCALLBACK, c_void_p]
|
||||
|
||||
# void launch_data_free(launch_data_t)
|
||||
launch_data_free = libc.launch_data_free
|
||||
launch_data_free.restype = None
|
||||
launch_data_free.argtypes = [c_launch_data_t]
|
||||
|
||||
# int launch_data_get_errno(const launch_data_t)
|
||||
launch_data_get_errno = libc.launch_data_get_errno
|
||||
launch_data_get_errno.restype = c_int
|
||||
launch_data_get_errno.argtypes = [c_launch_data_t]
|
||||
|
||||
# int launch_data_get_fd(const launch_data_t)
|
||||
launch_data_get_fd = libc.launch_data_get_fd
|
||||
launch_data_get_fd.restype = c_int
|
||||
launch_data_get_fd.argtypes = [c_launch_data_t]
|
||||
|
||||
# launch_data_type_t launch_data_get_type(const launch_data_t)
|
||||
launch_data_get_type = libc.launch_data_get_type
|
||||
launch_data_get_type.restype = c_launch_data_t
|
||||
launch_data_get_type.argtypes = [c_launch_data_t]
|
||||
|
||||
# launch_data_t launch_data_new_string(const char *)
|
||||
launch_data_new_string = libc.launch_data_new_string
|
||||
launch_data_new_string.restype = c_launch_data_t
|
||||
launch_data_new_string.argtypes = [c_char_p]
|
||||
|
||||
# launch_data_t launch_msg(const launch_data_t)
|
||||
launch_msg = libc.launch_msg
|
||||
launch_msg.restype = c_launch_data_t
|
||||
launch_msg.argtypes = [c_launch_data_t]
|
||||
|
||||
|
||||
LAUNCH_KEY_SUBMITJOB = c_char_p("SubmitJob")
|
||||
LAUNCH_KEY_REMOVEJOB = c_char_p("RemoveJob")
|
||||
LAUNCH_KEY_STARTJOB = c_char_p("StartJob")
|
||||
LAUNCH_KEY_STOPJOB = c_char_p("StopJob")
|
||||
LAUNCH_KEY_GETJOB = c_char_p("GetJob")
|
||||
LAUNCH_KEY_GETJOBS = c_char_p("GetJobs")
|
||||
LAUNCH_KEY_CHECKIN = c_char_p("CheckIn")
|
||||
|
||||
LAUNCH_JOBKEY_LABEL = c_char_p("Label")
|
||||
LAUNCH_JOBKEY_DISABLED = c_char_p("Disabled")
|
||||
LAUNCH_JOBKEY_USERNAME = c_char_p("UserName")
|
||||
LAUNCH_JOBKEY_GROUPNAME = c_char_p("GroupName")
|
||||
LAUNCH_JOBKEY_TIMEOUT = c_char_p("TimeOut")
|
||||
LAUNCH_JOBKEY_EXITTIMEOUT = c_char_p("ExitTimeOut")
|
||||
LAUNCH_JOBKEY_INITGROUPS = c_char_p("InitGroups")
|
||||
LAUNCH_JOBKEY_SOCKETS = c_char_p("Sockets")
|
||||
LAUNCH_JOBKEY_MACHSERVICES = c_char_p("MachServices")
|
||||
LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES = c_char_p("MachServiceLookupPolicies")
|
||||
LAUNCH_JOBKEY_INETDCOMPATIBILITY = c_char_p("inetdCompatibility")
|
||||
LAUNCH_JOBKEY_ENABLEGLOBBING = c_char_p("EnableGlobbing")
|
||||
LAUNCH_JOBKEY_PROGRAMARGUMENTS = c_char_p("ProgramArguments")
|
||||
LAUNCH_JOBKEY_PROGRAM = c_char_p("Program")
|
||||
LAUNCH_JOBKEY_ONDEMAND = c_char_p("OnDemand")
|
||||
LAUNCH_JOBKEY_KEEPALIVE = c_char_p("KeepAlive")
|
||||
LAUNCH_JOBKEY_LIMITLOADTOHOSTS = c_char_p("LimitLoadToHosts")
|
||||
LAUNCH_JOBKEY_LIMITLOADFROMHOSTS = c_char_p("LimitLoadFromHosts")
|
||||
LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE = c_char_p("LimitLoadToSessionType")
|
||||
LAUNCH_JOBKEY_RUNATLOAD = c_char_p("RunAtLoad")
|
||||
LAUNCH_JOBKEY_ROOTDIRECTORY = c_char_p("RootDirectory")
|
||||
LAUNCH_JOBKEY_WORKINGDIRECTORY = c_char_p("WorkingDirectory")
|
||||
LAUNCH_JOBKEY_ENVIRONMENTVARIABLES = c_char_p("EnvironmentVariables")
|
||||
LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES = c_char_p("UserEnvironmentVariables")
|
||||
LAUNCH_JOBKEY_UMASK = c_char_p("Umask")
|
||||
LAUNCH_JOBKEY_NICE = c_char_p("Nice")
|
||||
LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST = c_char_p("HopefullyExitsFirst")
|
||||
LAUNCH_JOBKEY_HOPEFULLYEXITSLAST = c_char_p("HopefullyExitsLast")
|
||||
LAUNCH_JOBKEY_LOWPRIORITYIO = c_char_p("LowPriorityIO")
|
||||
LAUNCH_JOBKEY_SESSIONCREATE = c_char_p("SessionCreate")
|
||||
LAUNCH_JOBKEY_STARTONMOUNT = c_char_p("StartOnMount")
|
||||
LAUNCH_JOBKEY_SOFTRESOURCELIMITS = c_char_p("SoftResourceLimits")
|
||||
LAUNCH_JOBKEY_HARDRESOURCELIMITS = c_char_p("HardResourceLimits")
|
||||
LAUNCH_JOBKEY_STANDARDINPATH = c_char_p("StandardInPath")
|
||||
LAUNCH_JOBKEY_STANDARDOUTPATH = c_char_p("StandardOutPath")
|
||||
LAUNCH_JOBKEY_STANDARDERRORPATH = c_char_p("StandardErrorPath")
|
||||
LAUNCH_JOBKEY_DEBUG = c_char_p("Debug")
|
||||
LAUNCH_JOBKEY_WAITFORDEBUGGER = c_char_p("WaitForDebugger")
|
||||
LAUNCH_JOBKEY_QUEUEDIRECTORIES = c_char_p("QueueDirectories")
|
||||
LAUNCH_JOBKEY_WATCHPATHS = c_char_p("WatchPaths")
|
||||
LAUNCH_JOBKEY_STARTINTERVAL = c_char_p("StartInterval")
|
||||
LAUNCH_JOBKEY_STARTCALENDARINTERVAL = c_char_p("StartCalendarInterval")
|
||||
LAUNCH_JOBKEY_BONJOURFDS = c_char_p("BonjourFDs")
|
||||
LAUNCH_JOBKEY_LASTEXITSTATUS = c_char_p("LastExitStatus")
|
||||
LAUNCH_JOBKEY_PID = c_char_p("PID")
|
||||
LAUNCH_JOBKEY_THROTTLEINTERVAL = c_char_p("ThrottleInterval")
|
||||
LAUNCH_JOBKEY_LAUNCHONLYONCE = c_char_p("LaunchOnlyOnce")
|
||||
LAUNCH_JOBKEY_ABANDONPROCESSGROUP = c_char_p("AbandonProcessGroup")
|
||||
LAUNCH_JOBKEY_IGNOREPROCESSGROUPATSHUTDOWN = c_char_p(
|
||||
"IgnoreProcessGroupAtShutdown")
|
||||
LAUNCH_JOBKEY_POLICIES = c_char_p("Policies")
|
||||
LAUNCH_JOBKEY_ENABLETRANSACTIONS = c_char_p("EnableTransactions")
|
||||
|
||||
LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS = c_char_p("DenyCreatingOtherJobs")
|
||||
|
||||
LAUNCH_JOBINETDCOMPATIBILITY_WAIT = c_char_p("Wait")
|
||||
|
||||
LAUNCH_JOBKEY_MACH_RESETATCLOSE = c_char_p("ResetAtClose")
|
||||
LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN = c_char_p("HideUntilCheckIn")
|
||||
LAUNCH_JOBKEY_MACH_DRAINMESSAGESONCRASH = c_char_p("DrainMessagesOnCrash")
|
||||
|
||||
LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT = c_char_p("SuccessfulExit")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE = c_char_p("NetworkState")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE = c_char_p("PathState")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE = c_char_p("OtherJobActive")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED = c_char_p("OtherJobEnabled")
|
||||
LAUNCH_JOBKEY_KEEPALIVE_AFTERINITIALDEMAND = c_char_p("AfterInitialDemand")
|
||||
|
||||
LAUNCH_JOBKEY_CAL_MINUTE = c_char_p("Minute")
|
||||
LAUNCH_JOBKEY_CAL_HOUR = c_char_p("Hour")
|
||||
LAUNCH_JOBKEY_CAL_DAY = c_char_p("Day")
|
||||
LAUNCH_JOBKEY_CAL_WEEKDAY = c_char_p("Weekday")
|
||||
LAUNCH_JOBKEY_CAL_MONTH = c_char_p("Month")
|
||||
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_CORE = c_char_p("Core")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_CPU = c_char_p("CPU")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_DATA = c_char_p("Data")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE = c_char_p("FileSize")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK = c_char_p("MemoryLock")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE = c_char_p("NumberOfFiles")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_NPROC = c_char_p("NumberOfProcesses")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_RSS = c_char_p("ResidentSetSize")
|
||||
LAUNCH_JOBKEY_RESOURCELIMIT_STACK = c_char_p("Stack")
|
||||
|
||||
LAUNCH_JOBKEY_DISABLED_MACHINETYPE = c_char_p("MachineType")
|
||||
LAUNCH_JOBKEY_DISABLED_MODELNAME = c_char_p("ModelName")
|
||||
|
||||
LAUNCH_JOBSOCKETKEY_TYPE = c_char_p("SockType")
|
||||
LAUNCH_JOBSOCKETKEY_PASSIVE = c_char_p("SockPassive")
|
||||
LAUNCH_JOBSOCKETKEY_BONJOUR = c_char_p("Bonjour")
|
||||
LAUNCH_JOBSOCKETKEY_SECUREWITHKEY = c_char_p("SecureSocketWithKey")
|
||||
LAUNCH_JOBSOCKETKEY_PATHNAME = c_char_p("SockPathName")
|
||||
LAUNCH_JOBSOCKETKEY_PATHMODE = c_char_p("SockPathMode")
|
||||
LAUNCH_JOBSOCKETKEY_NODENAME = c_char_p("SockNodeName")
|
||||
LAUNCH_JOBSOCKETKEY_SERVICENAME = c_char_p("SockServiceName")
|
||||
LAUNCH_JOBSOCKETKEY_FAMILY = c_char_p("SockFamily")
|
||||
LAUNCH_JOBSOCKETKEY_PROTOCOL = c_char_p("SockProtocol")
|
||||
LAUNCH_JOBSOCKETKEY_MULTICASTGROUP = c_char_p("MulticastGroup")
|
||||
|
||||
|
||||
(
|
||||
LAUNCH_DATA_DICTIONARY,
|
||||
LAUNCH_DATA_ARRAY,
|
||||
LAUNCH_DATA_FD,
|
||||
LAUNCH_DATA_INTEGER,
|
||||
LAUNCH_DATA_REAL,
|
||||
LAUNCH_DATA_BOOL,
|
||||
LAUNCH_DATA_STRING,
|
||||
LAUNCH_DATA_OPAQUE,
|
||||
LAUNCH_DATA_ERRNO,
|
||||
LAUNCH_DATA_MACHPORT
|
||||
) = range(1, 11)
|
||||
|
||||
|
||||
class LaunchDCheckInError(Exception):
|
||||
'''Exception to raise if there is a checkin error'''
|
||||
pass
|
||||
|
||||
def get_launchd_socket_fds():
|
||||
"""Check in with launchd to get socket file descriptors."""
|
||||
|
||||
# Return a dictionary with keys pointing to lists of file descriptors.
|
||||
launchd_socket_fds = dict()
|
||||
|
||||
def add_socket(launch_array, name, context=None):
|
||||
'''Callback for dict iterator.'''
|
||||
if launch_data_get_type(launch_array) != LAUNCH_DATA_ARRAY:
|
||||
raise LaunchDCheckInError(
|
||||
"Could not get file descriptor array: Type mismatch")
|
||||
fds = list()
|
||||
for i in range(launch_data_array_get_count(launch_array)):
|
||||
data_fd = launch_data_array_get_index(launch_array, i)
|
||||
if launch_data_get_type(data_fd) != LAUNCH_DATA_FD:
|
||||
raise LaunchDCheckInError(
|
||||
"Could not get file descriptor array entry: Type mismatch")
|
||||
fds.append(launch_data_get_fd(data_fd))
|
||||
launchd_socket_fds[name] = fds
|
||||
|
||||
# Wrap in try/finally to free resources allocated during lookup.
|
||||
try:
|
||||
# Create a checkin request.
|
||||
checkin_request = launch_data_new_string(LAUNCH_KEY_CHECKIN)
|
||||
if checkin_request is None:
|
||||
raise LaunchDCheckInError("Could not create checkin request")
|
||||
|
||||
# Check the checkin response.
|
||||
checkin_response = launch_msg(checkin_request)
|
||||
if checkin_response is None:
|
||||
raise LaunchDCheckInError("Error checking in")
|
||||
|
||||
if launch_data_get_type(checkin_response) == LAUNCH_DATA_ERRNO:
|
||||
errno = launch_data_get_errno(checkin_response)
|
||||
raise LaunchDCheckInError("Checkin failed")
|
||||
|
||||
# Get a dictionary of sockets.
|
||||
sockets = launch_data_dict_lookup(
|
||||
checkin_response, LAUNCH_JOBKEY_SOCKETS)
|
||||
if sockets is None:
|
||||
raise LaunchDCheckInError(
|
||||
"Could not get socket dictionary from checkin response")
|
||||
|
||||
if launch_data_get_type(sockets) != LAUNCH_DATA_DICTIONARY:
|
||||
raise LaunchDCheckInError(
|
||||
"Could not get socket dictionary from checkin response: "
|
||||
"Type mismatch")
|
||||
|
||||
# Iterate over the items with add_socket callback.
|
||||
launch_data_dict_iterate(sockets, DICTITCALLBACK(add_socket), None)
|
||||
|
||||
return launchd_socket_fds
|
||||
|
||||
finally:
|
||||
if checkin_response is not None:
|
||||
launch_data_free(checkin_response)
|
||||
if checkin_request is not None:
|
||||
launch_data_free(checkin_request)
|
||||
@@ -12,19 +12,27 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
'''Python wrapper for updated launchd checkin API'''
|
||||
|
||||
import os
|
||||
# pylint: disable=wildcard-import
|
||||
# pylint: disable=unused-wildcard-import
|
||||
from ctypes import *
|
||||
# pylint: enable=unused-wildcard-import
|
||||
# pylint: enable=wildcard-import
|
||||
# pylint: disable=invalid-name
|
||||
libc = CDLL("/usr/lib/libc.dylib")
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
|
||||
# int launch_activate_socket(const char *name, int **fds, size_t *cnt)
|
||||
libc.launch_activate_socket.restype = c_int
|
||||
libc.launch_activate_socket.argtypes = [c_char_p, POINTER(POINTER(c_int)), POINTER(c_size_t)]
|
||||
libc.launch_activate_socket.argtypes = [c_char_p, POINTER(POINTER(c_int)),
|
||||
POINTER(c_size_t)]
|
||||
|
||||
|
||||
class LaunchDError(Exception):
|
||||
'''Exception to raise if there is a checkin error'''
|
||||
pass
|
||||
|
||||
def launch_activate_socket(name):
|
||||
@@ -36,8 +44,9 @@ def launch_activate_socket(name):
|
||||
cnt = c_size_t(0)
|
||||
err = libc.launch_activate_socket(name, byref(fds), byref(cnt))
|
||||
if err:
|
||||
raise LaunchDError("Failed to retrieve sockets from launchd: %s" % os.strerror(err))
|
||||
|
||||
raise LaunchDError("Failed to retrieve sockets from launchd: %s"
|
||||
% os.strerror(err))
|
||||
|
||||
# Return a list of file descriptors.
|
||||
return list(fds[x] for x in xrange(cnt.value))
|
||||
|
||||
Reference in New Issue
Block a user