mirror of
https://github.com/munki/munki.git
synced 2026-02-21 06:30:28 -06:00
144 lines
5.4 KiB
Python
144 lines
5.4 KiB
Python
# encoding: utf-8
|
|
#
|
|
# Copyright 2017-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.
|
|
"""
|
|
updatecheck.unused_software
|
|
|
|
Created by Greg Neagle on 2017-02-18.
|
|
|
|
Functions for removing unused optional install items
|
|
"""
|
|
from __future__ import absolute_import, print_function
|
|
|
|
# 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 AppKit import NSWorkspace
|
|
# pylint: enable=E0611
|
|
|
|
# our libs
|
|
from .. import app_usage
|
|
from .. import display
|
|
|
|
|
|
def bundleid_is_running(app_bundleid):
|
|
'''Returns a boolean indicating if the application with the given
|
|
bundleid is currently running.'''
|
|
workspace = NSWorkspace.sharedWorkspace()
|
|
running_apps = workspace.runningApplications()
|
|
for app in running_apps:
|
|
if app.bundleIdentifier() == app_bundleid:
|
|
return True
|
|
return False
|
|
|
|
|
|
def bundleids_from_installs_list(pkginfo_pl):
|
|
'''Extracts a list of application bundle_ids from the installs list of a
|
|
pkginfo item'''
|
|
installs_list = pkginfo_pl.get('installs', [])
|
|
bundle_ids = [item.get('CFBundleIdentifier') for item in installs_list
|
|
if (item.get('CFBundleIdentifier') and
|
|
item.get('type') == 'application'
|
|
or (item.get('type') == 'bundle' and
|
|
item.get('path', '').endswith('.app')))]
|
|
return bundle_ids
|
|
|
|
|
|
def should_be_removed(item_pl):
|
|
"""Determines if an optional install item should be removed due to lack of
|
|
use.
|
|
Returns a boolean."""
|
|
|
|
name = item_pl['name']
|
|
removal_info = item_pl.get('unused_software_removal_info')
|
|
# do we have unused_software_removal_info?
|
|
if not removal_info:
|
|
return False
|
|
|
|
display.display_debug1(
|
|
'\tChecking to see if %s should be removed due to lack of use...', name)
|
|
try:
|
|
removal_days = int(removal_info.get('removal_days', 0))
|
|
if removal_days < 1:
|
|
raise ValueError
|
|
except ValueError:
|
|
display.display_warning('Invalid removal_days: %s for item %s'
|
|
% (removal_info.get('removal_days'), name))
|
|
return False
|
|
|
|
display.display_debug1(
|
|
'\t\tNumber of days until removal is %s', removal_days)
|
|
usage = app_usage.ApplicationUsageQuery()
|
|
usage_data_days = usage.days_of_data()
|
|
if usage_data_days is None or usage_data_days < removal_days:
|
|
# we don't have usage data old enough to judge
|
|
display.display_debug1(
|
|
'\t\tApplication usage data covers fewer than %s days.',
|
|
removal_days)
|
|
return False
|
|
|
|
# check to see if we have an install request within the removal_days
|
|
days_since_install_request = usage.days_since_last_install_event(
|
|
'install', name)
|
|
if (days_since_install_request is not None and
|
|
days_since_install_request != -1 and
|
|
days_since_install_request <= removal_days):
|
|
display.display_debug1('\t\t%s had an install request %s days ago.',
|
|
name, days_since_install_request)
|
|
return False
|
|
|
|
# get list of application bundle_ids to check
|
|
if 'bundle_ids' in removal_info:
|
|
bundle_ids = removal_info['bundle_ids']
|
|
else:
|
|
# get application bundle_ids from installs list
|
|
bundle_ids = bundleids_from_installs_list(item_pl)
|
|
if not bundle_ids:
|
|
display.display_debug1('\\tNo application bundle_ids to check.')
|
|
return False
|
|
|
|
# now check each bundleid to see if it's currently running or has been
|
|
# activated in the past removal_days days
|
|
display.display_debug1('\t\tChecking bundle_ids: %s', bundle_ids)
|
|
for bundle_id in bundle_ids:
|
|
if bundleid_is_running(bundle_id):
|
|
display.display_debug1(
|
|
'\t\tApplication %s is currently running.' % bundle_id)
|
|
return False
|
|
days_since_last_activation = usage.days_since_last_usage_event(
|
|
'activate', bundle_id)
|
|
if days_since_last_activation == -1:
|
|
display.display_debug1(
|
|
'\t\t%s has not been activated in more than %s days...',
|
|
bundle_id, usage.days_of_data())
|
|
elif days_since_last_activation <= removal_days:
|
|
display.display_debug1('\t\t%s was last activated %s days ago',
|
|
bundle_id, days_since_last_activation)
|
|
return False
|
|
else:
|
|
display.display_debug1('\t\t%s was last activated %s days ago',
|
|
bundle_id, days_since_last_activation)
|
|
|
|
# if we get this far we must not have found any apps used in the past
|
|
# removal_days days, so we should set up a removal
|
|
display.display_info('Will add %s to the removal list since it has been '
|
|
'unused for at least %s days...', name, removal_days)
|
|
return True
|
|
|
|
|
|
if __name__ == '__main__':
|
|
print('This is a library of support tools for the Munki Suite.')
|