#!/usr/bin/python from gi.repository import Gtk from gi.repository import GLib from gi.repository import Gio from gi.repository import AppIndicator3 as appindicator 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()