#!/usr/bin/python import gi gi.require_version('Gtk','3.0') from gi.repository import Gtk from gi.repository import GLib from gi.repository import Gio gi.require_version('AppIndicator3','0.1') from gi.repository import AppIndicator3 as appindicator gi.require_version('Notify','0.7') from gi.repository import Notify import sys import signal import time import os import grp import subprocess from aptdaemon import client import gettext gettext.textdomain('lliurex-upgrade-indicator') _ = gettext.gettext signal.signal(signal.SIGINT, signal.SIG_DFL) #admins,adm class UpgradeIndicator: ID="net.lliurex.UpgradeIndicator" COMMAND="gksu lliurex-up" GROUPS=["admins","adm"] def __init__(self,debug): self.is_cache_updated=True #whenever is ready to check for new upgrades self.is_working=False self.last_check=0 self.debug=debug #Get Settings and connect to changes self.settings = Gio.Settings.new("net.lliurex.UpgradeIndicator") self.settings.connect("changed::enabled",self.on_setting_enabled_changed) self.settings.connect("changed::check-frequency",self.on_setting_frequency_changed) self.FREQUENCY=self.settings.get_int("check-frequency") self.ENABLED=self.settings.get_boolean("enabled") if(self.ENABLED==False): self.log("Indicator has been disabled from gsettings") sys.exit(0) #check if the user is in the enabled group, otherwise the indicator is closed user=os.environ["USER"] group_found=False for g in grp.getgrall(): if(g.gr_name in UpgradeIndicator.GROUPS): for member in g.gr_mem: if(member==user): group_found=True break if (group_found==False): self.log("User does not belong to group {0}".format(UpgradeIndicator.GROUPS)) sys.exit(0) #Creates an app indicator and hides it self.indicator = appindicator.Indicator.new(UpgradeIndicator.ID,"software-update-available",appindicator.IndicatorCategory.SYSTEM_SERVICES) self.indicator.set_status(appindicator.IndicatorStatus.PASSIVE) #Initializes Notify system Notify.init(UpgradeIndicator.ID) #creates a simple menu menu = Gtk.Menu() item = Gtk.MenuItem.new_with_label(_("Upgrade now")) item.connect("activate",self.on_item_upgrade_clicked) item.show() menu.add(item) self.indicator.set_menu(menu) #Watch apt cache database for changes self.fcache=Gio.File.new_for_path("/var/cache/apt/pkgcache.bin") self.mcache=self.fcache.monitor_file(Gio.FileMonitorFlags.NONE,None) self.mcache.connect("changed",self.on_cache_changed) #time-out thread GLib.timeout_add_seconds(5, self.worker) #def def worker(self): """ Timeout thread """ if(self.is_working==False): if(self.is_cache_updated==True): self.upgrade() self.last_check=0 else: self.last_check+=5 if(self.last_check>self.FREQUENCY): self.last_check=0 self.upgrade() return True #def #GSettings change hooks def on_setting_enabled_changed(self,settings,key): self.ENABLED=self.settings.get_boolean("enabled") #def def on_setting_frequency_changed(self,settings,key): self.FREQUENCY=self.settings.get_int("check-frequency") #def def on_item_upgrade_clicked(self,data): """Indicator click event""" self.log("Launching cmd: {0}".format(UpgradeIndicator.COMMAND)) self.indicator.set_status(appindicator.IndicatorStatus.PASSIVE) self.notify.close() args=UpgradeIndicator.COMMAND.split(" ") subprocess.Popen(args) #def def on_cache_changed(self,monitor,file,other,type): """Apt cache notification event""" if(type==Gio.FileMonitorEvent.CHANGES_DONE_HINT and self.last_check>120): #ignore cache updates at intervals smaller than 2 minutes self.is_cache_updated=True self.log("Cache has been updated") #def def upgrade(self): """ Performs an upgrade simulation. Cache will be updated if needed. """ self.is_working=True apt_client=client.AptClient() if(self.is_cache_updated==False): self.log("Updating cache...") apt_client.update_cache(wait=True) self.is_cache_updated=True self.log("Cache updated") self.log("Checking for upgrades...") transaction=apt_client.upgrade_system() transaction.simulate() #this sync is needed in order to update transaction properties after simulation #credits go to Lubuntu Team as this is completly undocumented :) transaction.sync() #transaction dependencies [] # install reinstall remove purge upgrade downgrade packages=[] for d in transaction.dependencies: for p in d: packages.append(d) self.log("packages to upgrade:{0}".format(len(packages))) if(len(packages)>0): self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) self.notify=Notify.Notification.new(_("Upgrades available"), _("There are {0} packages ready to be updated").format(len(packages)), "software-update-available") self.notify.set_hint("transient", GLib.Variant.new_boolean(True)) self.notify.show() self.is_working=False self.is_cache_updated=False #def def log(self,txt): """Simple print function""" if(self.debug): print(txt) #def #class def print_usage(): print("Usage:llx-upgrade-indicator [OPTIONS]") print("--help shows this message") print("--debug enable some verbosity on stdout") #def if __name__ == "__main__": debug=False args=sys.argv[1:] for arg in args: if(arg=="--help"): print_usage() sys.exit(0) elif(arg=="--debug"): debug=True else: print_usage() sys.exit(0) upi=UpgradeIndicator(debug) Gtk.main()