Viewing file: yum-updatesd (10.52 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) | #!/usr/bin/python -tt
#
# Proof of concept yumd implementation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright 2007 Red Hat, Inc.
# Jeremy Katz
#
# since it takes me time everytime to figure this out again, here's how to
# queue a check with dbus-send. adjust appropriately for other methods
# $ dbus-send --system --print-reply --type=method_call \
# --dest=edu.duke.linux.yum /Updatesd edu.duke.linux.yum.CheckNow
import os, sys
import string
import syslog
import string
import subprocess
import time # For updaterefresh
from optparse import OptionParser
import dbus
import dbus.service
import dbus.glib
import gobject
import gamin
from yum.config import BaseConfig, Option, IntOption, ListOption, BoolOption
try:
from yum.config import SecondsOption
except:
class SecondsOption(IntOption):
pass
from yum.parser import ConfigPreProcessor
from ConfigParser import ConfigParser, ParsingError
updateInfoDone = False
updateInfo = []
helperProcess = None
NM_ONLINE = 3
class UDConfig(BaseConfig):
"""Config format for the daemon"""
run_interval = SecondsOption(60 * 60) # 1h
nonroot_workdir = Option("/var/tmp/yum-updatesd")
emit_via = ListOption(['dbus', 'email', 'syslog'])
email_to = ListOption(["root"])
email_from = Option("root")
smtp_server = Option("localhost:25")
use_sendmail = BoolOption(True)
dbus_listener = BoolOption(True)
do_update = BoolOption(False)
do_download = BoolOption(False)
do_download_deps = BoolOption(False)
updaterefresh = SecondsOption(60 * 60) # 1h
syslog_facility = Option("DAEMON")
syslog_level = Option("WARN")
syslog_ident = Option("yum-updatesd")
yum_config = Option("/etc/yum/yum.conf")
# added debugging
debug = BoolOption(False)
def read_config():
confparser = ConfigParser()
opts = UDConfig()
config_file = '/etc/yum/yum-updatesd.conf'
if os.path.exists(config_file):
confpp_obj = ConfigPreProcessor(config_file)
try:
confparser.readfp(confpp_obj)
except ParsingError, e:
print >> sys.stderr, "Error reading config file: %s" % e
sys.exit(1)
opts.populate(confparser, 'main')
return opts
class YumDbusListener(dbus.service.Object):
def __init__(self, bus_name, object_path='/Updatesd',
allowshutdown = False, config = None):
dbus.service.Object.__init__(self, bus_name, object_path)
self.allowshutdown = allowshutdown
self.yumdconfig = config
def doCheck(self):
checkUpdates(self.yumdconfig, limited=True)
return False
@dbus.service.method("edu.duke.linux.yum", in_signature="")
def CheckNow(self):
# make updating checking asynchronous since we discover whether
# or not there are updates via a callback signal anyway
gobject.idle_add(self.doCheck)
return "check queued"
@dbus.service.method("edu.duke.linux.yum", in_signature="")
def ShutDown(self):
if not self.allowshutdown:
return False
# we have to do this in a callback so that it doesn't get
# sent back to the caller
gobject.idle_add(sys.exit, 0)
return True
@dbus.service.method("edu.duke.linux.yum", in_signature="", out_signature="a(a{ss}a{ss})")
def GetUpdateInfo(self):
# FIXME: this call is deprecated. things should watch for the update
# info signals
global updateInfoDone, updateInfo
if not updateInfoDone:
# FIXME: this isn't synchronous anymore. but it should be
# reasonable enough given the users
gobject.idle_add(self.doCheck)
return []
return updateInfo
def checkHelperStatus():
global helperProcess
if helperProcess.poll() is not None:
helperProcess = None
return False
return True
lastUpdate = None
def checkUpdates(opts, wait = False, limited=False):
""" Run yum-updatesd-helper to check for updates and report. Possibly
wait for the result, and/or limit the number of updates we try. """
global lastUpdate
global helperProcess
if helperProcess is not None:
print >> sys.stderr, "Helper process already running"
return True
now = time.time()
if limited and lastUpdate and (now - lastUpdate) < opts.updaterefresh:
print >> sys.stderr, "Update requested too quickly"
return True
if os.path.exists("./yum-updatesd-helper") and opts.debug:
args = ["./yum-updatesd-helper", "--check"]
else:
args = ["/usr/libexec/yum-updatesd-helper", "--check"]
bus = dbus.SystemBus()
try:
o = bus.get_object("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager")
if o.state() != NM_ONLINE:
args.append("--network-fail")
except dbus.DBusException:
pass
# arguments for what to do
if opts.do_download:
args.append("--download")
if opts.do_download_deps:
args.append("--deps")
if opts.do_update:
args.append("--apply")
# how should we send notifications?
if "dbus" in opts.emit_via:
args.append("--dbus")
if "email" in opts.emit_via:
args.extend(["--email", "--email-from=%s" %(opts.email_from,),
"--email-to=%s" %(string.join(opts.email_to, ","),),
"--smtp-server=%s" %(opts.smtp_server)])
if opts.use_sendmail:
args.append("--sendmail")
if "syslog" in opts.emit_via:
args.extend(["--syslog", "--syslog-level=%s" %(opts.syslog_level,),
"--syslog-facility=%s" %(opts.syslog_facility,),
"--syslog-ident=%s" %(opts.syslog_ident,)])
if opts.debug:
args.append("--debug")
print >> sys.stderr, "Going to exec: %s" %(args,)
lastUpdate = now
helperProcess = subprocess.Popen(args, close_fds = True)
if not wait:
gobject.timeout_add(1 * 1000, checkHelperStatus)
return True
return helperProcess.wait()
def add_update(update):
global updateInfo, updateInfoDone
if updateInfoDone:
updateInfo = []
updateInfoDone = False
updateInfo.append(update)
def updates_done(num):
global updateInfoDone, updateInfo
# if we received all the updates, we're good. otherwise, we need to
# clear the info out
if int(num) != len(updateInfo):
updateInfo = []
else:
updateInfoDone = True
def updates_applied(*args):
global updateInfoDone, updateInfo
updateInfo = []
updateInfoDone = False
def invalidate_cache(*args):
global updateInfoDone, updateInfo, helperProcess
if helperProcess is not None:
return
updateInfo = []
updateInfoDone = False
def setup_watcher():
"""Sets up gamin-based file watches on things we care about and makes
it so they get checked every 15 seconds."""
# add some watches on directories.
mon = gamin.WatchMonitor()
mon.watch_directory("/var/lib/rpm", invalidate_cache)
mon.watch_directory("/var/cache/yum", invalidate_cache)
map(lambda x: os.path.isdir("/var/cache/yum/%s" %(x,)) and
mon.watch_directory("/var/cache/yum/%s" %(x,), invalidate_cache),
os.listdir("/var/cache/yum"))
mon.handle_events()
fd = mon.get_fd()
gobject.io_add_watch(fd, gobject.IO_IN|gobject.IO_PRI,
lambda x, y: mon.handle_events())
def network_state_change(newstate, opts):
if int(newstate) == NM_ONLINE:
checkUpdates(opts, limited=True)
def main(options = None):
if options is None:
parser = OptionParser()
parser.add_option("-d", "--debug", action="store_true", default=False, dest="debug")
parser.add_option("-f", "--no-fork", action="store_true", default=False, dest="nofork")
parser.add_option("-o", "--oneshot", action="store_true", default=False, dest="oneshot")
parser.add_option("-r", "--remote-shutdown", action="store_true", default=False, dest="remoteshutdown")
(options, args) = parser.parse_args()
if not options.oneshot and not options.nofork:
if os.fork():
sys.exit()
os.chdir("/")
fd = os.open("/dev/null", os.O_RDWR)
os.dup2(fd, 0)
os.dup2(fd, 1)
os.dup2(fd, 2)
os.close(fd)
syslog.openlog("yum-updatesd", 0, syslog.LOG_DAEMON)
opts = read_config()
if options.debug:
opts.debug = True
if options.oneshot:
checkUpdates(opts, wait = True)
sys.exit(0)
try:
bus = dbus.SystemBus()
except dbus.DBusException, e:
bus = None
if opts.dbus_listener and bus is not None:
name = dbus.service.BusName("edu.duke.linux.yum", bus=bus)
YumDbusListener(name, allowshutdown = options.remoteshutdown,
config = opts)
bus.add_signal_receiver(add_update, "UpdateInfoSignal", dbus_interface="edu.duke.linux.yum")
bus.add_signal_receiver(updates_done, "UpdatesAvailableSignal", dbus_interface="edu.duke.linux.yum")
bus.add_signal_receiver(updates_applied, "UpdatesAppliedSignal", dbus_interface="edu.duke.linux.yum")
try:
if bus is not None:
bus.add_signal_receiver(lambda x: network_state_change(x, opts),
"StateChange",
"org.freedesktop.NetworkManager")
except Exception, e:
pass
run_interval_ms = opts.run_interval * 1000 # needs to be in ms
# Note that we don't use limited=True here because:
# 1. We could get out of sync. with yum metadata_expire, causing the yum
# UI to hit the network.
# 2. If updatesrefresh == run_interval (the default), we could skip every
# other timeout.
gobject.timeout_add(run_interval_ms, checkUpdates, opts)
# set up file watcher when we're idle
gobject.idle_add(setup_watcher)
mainloop = gobject.MainLoop()
mainloop.run()
if __name__ == "__main__":
main()
|