mirror of
https://github.com/munki/munki.git
synced 2026-01-23 23:49:22 -06:00
This change is still a good future goal, but is causing problems that are too difficult to work around right now and is delaying the vital release of Munki 5.1 for Big Sur compatibility.
This reverts commit 3bb91cabca.
219 lines
7.9 KiB
Python
Executable File
219 lines
7.9 KiB
Python
Executable File
#!/usr/local/munki/python
|
|
# encoding: utf-8
|
|
#
|
|
# Copyright 2011-2020 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.
|
|
"""
|
|
logouthelper
|
|
|
|
Created by Greg Neagle on 2011-06-21.
|
|
|
|
A helper tool for forced logouts to allow munki to force install items by
|
|
a certain deadline.
|
|
|
|
"""
|
|
from __future__ import absolute_import
|
|
|
|
# standard libs
|
|
import os
|
|
import time
|
|
|
|
# our libs
|
|
from munkilib import info
|
|
from munkilib import munkilog
|
|
from munkilib import osutils
|
|
from munkilib import prefs
|
|
from munkilib import processes
|
|
from munkilib import FoundationPlist
|
|
|
|
# Apple frameworks via PyObjC
|
|
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
|
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
|
# pylint: disable=E0611
|
|
from Foundation import NSDate
|
|
from Foundation import NSDictionary
|
|
from Foundation import NSDistributedNotificationCenter
|
|
from Foundation import NSNotificationDeliverImmediately
|
|
from Foundation import NSNotificationPostToAllSessions
|
|
# pylint: enable=E0611
|
|
|
|
|
|
NOTIFICATION_MINS = [240, 180, 120, 90, 60, 45, 30, 15, 10, 5]
|
|
MANDATORY_NOTIFICATIONS = [60, 30, 10, 5]
|
|
PROCESS_ID = 'com.googlecode.munki.logouthelper'
|
|
|
|
|
|
def log(msg):
|
|
'''Logs messages from this tool with an identifier'''
|
|
munkilog.log('%s: %s' % (PROCESS_ID, msg))
|
|
|
|
|
|
def earliest_force_install_date():
|
|
'''Check installable packages for force_install_after_dates
|
|
Returns None or earliest force_install_after_date converted to local time
|
|
'''
|
|
earliest_date = None
|
|
|
|
managed_install_dir = prefs.pref('ManagedInstallDir')
|
|
installinfo_types = {
|
|
'InstallInfo.plist' : 'managed_installs',
|
|
'AppleUpdates.plist': 'AppleUpdates'
|
|
}
|
|
installinfopath = os.path.join(managed_install_dir, 'InstallInfo.plist')
|
|
|
|
try:
|
|
installinfo = FoundationPlist.readPlist(installinfopath)
|
|
except FoundationPlist.NSPropertyListSerializationException:
|
|
return None
|
|
|
|
for plist_name in installinfo_types:
|
|
key_to_check = installinfo_types[plist_name]
|
|
plist_path = os.path.join(managed_install_dir, plist_name)
|
|
try:
|
|
installinfo = FoundationPlist.readPlist(plist_path)
|
|
except FoundationPlist.NSPropertyListSerializationException:
|
|
continue
|
|
|
|
for install in installinfo.get(key_to_check, []):
|
|
force_install_date = install.get('force_install_after_date')
|
|
|
|
if force_install_date:
|
|
force_install_date = info.subtract_tzoffset_from_date(
|
|
force_install_date)
|
|
if not earliest_date or force_install_date < earliest_date:
|
|
earliest_date = force_install_date
|
|
|
|
return earliest_date
|
|
|
|
|
|
def alert_user_of_forced_logout(info_dict=None):
|
|
'''Uses Managed Software Center.app to notify the user of an
|
|
upcoming forced logout.
|
|
|
|
Args:
|
|
info: dict of data to send with the notification.
|
|
'''
|
|
consoleuser = osutils.getconsoleuser()
|
|
if not processes.find_processes(
|
|
exe="/Applications/Managed Software Center.app",
|
|
user=consoleuser):
|
|
# Managed Software Center.app isn't running.
|
|
# Use our LaunchAgent to start
|
|
# Managed Software Center.app in the user context.
|
|
launchfile = '/var/run/com.googlecode.munki.ManagedSoftwareCenter'
|
|
fileref = open(launchfile, 'w')
|
|
fileref.close()
|
|
# now wait a bit for it to launch before proceeding
|
|
# because if we don't, sending the logoutwarn notification
|
|
# may fall on deaf ears.
|
|
time.sleep(5)
|
|
if os.path.exists(launchfile):
|
|
os.unlink(launchfile)
|
|
|
|
# if set, convert Python dictionary to NSDictionary.
|
|
if info_dict is not None:
|
|
info_dict = NSDictionary.dictionaryWithDictionary_(info_dict)
|
|
# cause MSC.app to display the Forced Logout warning
|
|
dnc = NSDistributedNotificationCenter.defaultCenter()
|
|
dnc.postNotificationName_object_userInfo_options_(
|
|
'com.googlecode.munki.ManagedSoftwareUpdate.logoutwarn',
|
|
None, info_dict,
|
|
NSNotificationDeliverImmediately + NSNotificationPostToAllSessions)
|
|
|
|
# make sure flag is in place to cause munki to install at logout
|
|
fileref = open('/private/tmp/com.googlecode.munki.installatlogout', 'w')
|
|
fileref.close()
|
|
|
|
|
|
def main():
|
|
'''Check for logged-in users and upcoming forced installs;
|
|
notify the user if needed; sleep a minute and do it again.'''
|
|
if prefs.pref('LogToSyslog'):
|
|
munkilog.configure_syslog()
|
|
|
|
log('launched')
|
|
sent_notifications = []
|
|
logout_time_override = None
|
|
|
|
# datetime of now plus largest MANDATORY_NOTIFICATIONS value (with padding).
|
|
minimum_notifications_logout_time = NSDate.date().addTimeInterval_(
|
|
60 * max(MANDATORY_NOTIFICATIONS) + 30)
|
|
while True:
|
|
if not osutils.currentGUIusers():
|
|
# no-one is logged in, so bail
|
|
log('no-one logged in')
|
|
time.sleep(10) # makes launchd happy
|
|
log('exited')
|
|
exit(0)
|
|
|
|
# we check each time because items might have been added or removed
|
|
# from the list; or their install date may have been changed.
|
|
next_logout_time = earliest_force_install_date()
|
|
if not next_logout_time:
|
|
# no forced logout needed, so bail
|
|
log('no forced installs found')
|
|
time.sleep(10) # makes launchd happy
|
|
log('exited')
|
|
exit(0)
|
|
|
|
if logout_time_override is None:
|
|
logout_time = next_logout_time
|
|
else:
|
|
# allow the new next_logout_time from InstallInfo to be used
|
|
# if it has changed to a later time since when we decided to
|
|
# override it.
|
|
if next_logout_time > logout_time_override:
|
|
logout_time = next_logout_time
|
|
log('reset logout_time to: %s' % logout_time)
|
|
logout_time_override = None
|
|
sent_notifications = []
|
|
|
|
# always give at least MANDATORY_NOTIFICATIONS warnings
|
|
if logout_time < minimum_notifications_logout_time:
|
|
for mandatory_notification in MANDATORY_NOTIFICATIONS:
|
|
if mandatory_notification not in sent_notifications:
|
|
# logout time is in the past, and a mandatory notification
|
|
# has not been sent, so reset the logout_time to the future.
|
|
log('%d minute notification not sent.'
|
|
% mandatory_notification)
|
|
logout_time = NSDate.date(
|
|
).addTimeInterval_(60 * mandatory_notification + 30)
|
|
log('reset logout_time to: %s' % logout_time)
|
|
logout_time_override = logout_time
|
|
break
|
|
|
|
minutes_until_logout = int(logout_time.timeIntervalSinceNow() / 60)
|
|
info_dict = {'logout_time': logout_time}
|
|
if minutes_until_logout in NOTIFICATION_MINS:
|
|
sent_notifications.append(minutes_until_logout)
|
|
log('Warning user of %s minutes until forced logout'
|
|
% minutes_until_logout)
|
|
alert_user_of_forced_logout(info_dict)
|
|
elif minutes_until_logout < 1:
|
|
log('Forced logout in 60 seconds')
|
|
alert_user_of_forced_logout(info_dict)
|
|
|
|
time.sleep(60)
|
|
if minutes_until_logout < 1:
|
|
break
|
|
|
|
if osutils.currentGUIusers() and earliest_force_install_date():
|
|
log('Beginning forced logout')
|
|
processes.force_logout_now()
|
|
log('exited')
|
|
exit(0)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|