Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion payload/usr/local/munki/postflight.d/sal-postflight
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,74 @@

import os
import subprocess
import sys
import socket
import urllib2

sys.path[:0] = ['/usr/local/munki', '/usr/local/sal']
import utils
from munkilib import FoundationPlist, munkicommon

TOUCH_FILE_PATH = '/Users/Shared/.com.salopensource.sal.run'
LAUNCHD = 'com.salopensource.sal.runner'
LAUNCHD_PATH = '/Library/LaunchDaemons/{}.plist'.format(LAUNCHD)
SUBMIT_SCRIPT = '/usr/local/sal/bin/sal-submit'


def check_for_errors(report):
"""Checks if the device was offline for last Munki run."""
errors = report.get("Errors", [])
warnings = report.get("Warnings", [])
target_errors = ["Could not retrieve managed install primary manifest."]
target_warnings = [
"Could not download Apple SUS catalog:",
"\t(-1009, u'The Internet connection appears to be offline.')"
]

using_sus = munkicommon.pref('InstallAppleSoftwareUpdates')

if using_sus:
if warnings == target_warnings and errors == target_errors:
return True
else:
if errors == target_errors:
return True

return False


def check_server_connection():
host = munkicommon.pref('SoftwareRepoURL')
try:
urllib2.urlopen(host, timeout=5).getcode()
except urllib2.HTTPError as e:
# If we get a http error, the server is returning _something_
return True
except urllib2.URLError as e:
return False
except socket.timeout as e:
return False
return True

def check_server_online():
# read report
report = utils.get_managed_install_report()
# check for errors and warnings
if not check_for_errors(report):
utils.set_pref('LastRunWasOffline', False)
return
# if they're there check is server is really offline
if not check_server_connection():
utils.set_pref('LastRunWasOffline', True)
return

# If we get here, it's online
utils.set_pref('LastRunWasOffline', False)

def write_touch_file():
if os.path.exists(TOUCH_FILE_PATH):
os.remove(TOUCH_FILE_PATH)

if not os.path.exists(TOUCH_FILE_PATH):
with open(TOUCH_FILE_PATH, 'a'):
os.utime(TOUCH_FILE_PATH, None)
Expand All @@ -33,6 +90,7 @@ def ensure_launchd_loaded():
subprocess.check_call(cmd)

def main():
check_server_online()
write_touch_file()
ensure_launchd_loaded()
# If the launchd isn't present, call the submit script old school
Expand Down
53 changes: 10 additions & 43 deletions payload/usr/local/sal/bin/sal-submit
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import yaml


BUNDLE_ID = 'com.github.salopensource.sal'
VERSION = '2.1.3'
VERSION = '2.1.4'

# To resolve unicode write errors set our default encoding to utf8
reload(sys)
Expand All @@ -47,12 +47,16 @@ def main():
other_sal_pid = utils.pythonScriptRunning('sal-submit')
if other_sal_pid:
sys.exit('Another instance of sal-submit is already running. Exiting.')

time.sleep(1)
munki_pid = utils.pythonScriptRunning('managedsoftwareupdate')
if munki_pid:
sys.exit('managedsoftwareupdate is running. Exiting.')
report = get_managed_install_report()
report = utils.get_managed_install_report()

if utils.pref('LastRunWasOffline'):
sys.exit('Device appears to have been offline during '
'last Munki run')
serial = report['MachineInfo'].get('serial_number')
if not serial:
sys.exit('Unable to get MachineInfo from ManagedInstallReport.plist. '
Expand Down Expand Up @@ -114,7 +118,7 @@ def main():
send_install(ServerURL, copy.copy(submission))
send_catalogs(ServerURL, copy.copy(submission))
send_profiles(ServerURL, copy.copy(submission))

touchfile = '/Users/Shared/.com.salopensource.sal.run'
if os.path.exists(touchfile):
os.remove(touchfile)
Expand Down Expand Up @@ -174,45 +178,14 @@ def get_server_prefs():
return (required_prefs["ServerURL"], name_type, required_prefs["key"])


def get_managed_install_report():
"""Return Munki ManagedInstallsReport.plist as a plist dict.

Returns:
ManagedInstalls report for last Munki run as a plist
dict, or an empty dict.
"""
# Checks munki preferences to see where the install directory is set to.
managed_install_dir = munkicommon.pref('ManagedInstallDir')

# set the paths based on munki's configuration.
managed_install_report = os.path.join(
managed_install_dir, 'ManagedInstallReport.plist')

munkicommon.display_debug2(
"Looking for munki's ManagedInstallReport.plist at {} ...".format(
managed_install_report))
try:
munki_report = FoundationPlist.readPlist(managed_install_report)
except FoundationPlist.FoundationPlistException:
munki_report = {}

if 'MachineInfo' not in munki_report:
munki_report['MachineInfo'] = {}

munkicommon.display_debug2('ManagedInstallReport.plist:')
munkicommon.display_debug2(format_plist(munki_report))

return munki_report


def get_plugin_results(plugin_results_plist):
""" Read external data plist if it exists and return a dict."""
result = {}
if os.path.exists(plugin_results_plist):
try:
plist_data = FoundationPlist.readPlist(plugin_results_plist)
munkicommon.display_debug2('External data plist:')
munkicommon.display_debug2(format_plist(plist_data))
munkicommon.display_debug2(utils.format_plist(plist_data))
result = plist_data
except FoundationPlist.NSPropertyListSerializationException:
munkicommon.display_debug2('Could not read external data plist.')
Expand All @@ -222,12 +195,6 @@ def get_plugin_results(plugin_results_plist):
return result


def format_plist(plist):
"""Format a plist as a string for debug output."""
# For now, just dump it.
return FoundationPlist.writePlistToString(plist)


def get_sys_profile():
"""Get sysprofiler info.

Expand All @@ -252,7 +219,7 @@ def get_sys_profile():

munkicommon.display_debug2(
'System Profiler SPNetworkDataType and SPHardwareDataType:')
munkicommon.display_debug2(format_plist(system_profile))
munkicommon.display_debug2(utils.format_plist(system_profile))

return system_profile

Expand Down
40 changes: 39 additions & 1 deletion payload/usr/local/sal/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import time

sys.path.insert(0, '/usr/local/munki')
from munkilib import FoundationPlist
from munkilib import FoundationPlist, munkicommon
from Foundation import kCFPreferencesAnyUser, \
kCFPreferencesCurrentHost, \
CFPreferencesSetValue, \
Expand Down Expand Up @@ -65,6 +65,7 @@ def pref(pref_name):
'BasicAuth': True,
'GetGrains': False,
'GetOhai': False,
'LastRunWasOffline': False,
}

pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID)
Expand All @@ -82,6 +83,43 @@ def pref(pref_name):
return pref_value


def get_managed_install_report():
"""Return Munki ManagedInstallsReport.plist as a plist dict.

Returns:
ManagedInstalls report for last Munki run as a plist
dict, or an empty dict.
"""
# Checks munki preferences to see where the install directory is set to.
managed_install_dir = munkicommon.pref('ManagedInstallDir')

# set the paths based on munki's configuration.
managed_install_report = os.path.join(
managed_install_dir, 'ManagedInstallReport.plist')

munkicommon.display_debug2(
"Looking for munki's ManagedInstallReport.plist at {} ...".format(
managed_install_report))
try:
munki_report = FoundationPlist.readPlist(managed_install_report)
except FoundationPlist.FoundationPlistException:
munki_report = {}

if 'MachineInfo' not in munki_report:
munki_report['MachineInfo'] = {}

munkicommon.display_debug2('ManagedInstallReport.plist:')
munkicommon.display_debug2(format_plist(munki_report))

return munki_report


def format_plist(plist):
"""Format a plist as a string for debug output."""
# For now, just dump it.
return FoundationPlist.writePlistToString(plist)


def pythonScriptRunning(scriptname):
"""
Tests if a script is running. If it is found running, it will try
Expand Down