[yum-cvs] Makefile docs/Makefile docs/yum-updatesd.8 docs/yum-updatesd.conf.5 etc/Makefile etc/yum-updatesd.init yum-updatesd.py yum.spec

James Bowes jbowes at linux.duke.edu
Mon Oct 1 12:21:57 UTC 2007


 Makefile                 |    7 
 docs/Makefile            |    2 
 docs/yum-updatesd.8      |   18 +
 docs/yum-updatesd.conf.5 |   70 +++++
 etc/Makefile             |   10 
 etc/yum-updatesd.init    |   63 ++++
 yum-updatesd.py          |  654 +++++++++++++++++++++++++++++++++++++++++++++++
 yum.spec                 |   37 ++
 8 files changed, 855 insertions(+), 6 deletions(-)

New commits:
commit a5ca701944afd5b870d8273752eafc0051993beb
Author: James Bowes <jbowes at redhat.com>
Date:   Mon Oct 1 08:20:12 2007 -0400

    Revert "Remove yum-updatesd, as it's a distinct project now."
    
    yum-updatesd is still needed for F7.
    
    This reverts commit bab47e934196b94a8afdca6d306806e46e73f4e3.

diff --git a/Makefile b/Makefile
index 9d9730b..62121e3 100644
--- a/Makefile
+++ b/Makefile
@@ -21,10 +21,12 @@ install:
 	for p in $(PYFILES) ; do \
 		install -m 644 $$p $(DESTDIR)/usr/share/yum-cli/$$p; \
 	done
+	mv $(DESTDIR)/usr/share/yum-cli/yum-updatesd.py $(DESTDIR)/usr/share/yum-cli/yumupd.py
 	$(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/usr/share/yum-cli', 1, '$(PYDIR)', 1)"
 
 	mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/usr/sbin
 	install -m 755 bin/yum.py $(DESTDIR)/usr/bin/yum
+	install -m 755 bin/yum-updatesd.py $(DESTDIR)/usr/sbin/yum-updatesd
 
 	mkdir -p $(DESTDIR)/var/cache/yum
 	mkdir -p $(DESTDIR)/var/lib/yum	
@@ -34,8 +36,9 @@ install:
 .PHONY: docs test
 docs:
 	epydoc -n yum -o docs/epydoc -u http://linux.duke.edu/projects/yum \
-			yum rpmUtils callback.py progress_meter.py yumcommands.py \
-			shell.py translate.py output.py i18n.py cli.py yummain.py
+			yum yum-updatesd.py rpmUtils callback.py progress_meter.py \
+			yumcommands.py shell.py translate.py output.py i18n.py cli.py \
+			yummain.py
 
 test:
 	python test/alltests.py
diff --git a/docs/Makefile b/docs/Makefile
index 1fc653f..379ba2b 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -10,3 +10,5 @@ install:
 	install -m 644 yum.8 $(DESTDIR)/usr/share/man/man8/yum.8
 	install -m 644 yum-shell.8 $(DESTDIR)/usr/share/man/man8/yum-shell.8
 	install -m 644 yum.conf.5 $(DESTDIR)/usr/share/man/man5/yum.conf.5
+	install -m 644 yum-updatesd.8 $(DESTDIR)/usr/share/man/man8/yum-updatesd.8
+	install -m 644 yum-updatesd.conf.5 $(DESTDIR)/usr/share/man/man5/yum-updatesd.conf.5
diff --git a/docs/yum-updatesd.8 b/docs/yum-updatesd.8
new file mode 100644
index 0000000..be0d346
--- /dev/null
+++ b/docs/yum-updatesd.8
@@ -0,0 +1,18 @@
+.TH "yum-updatesd" "8" "" "Jeremy Katz" ""
+.SH "NAME"
+yum-updatesd \- Update notifier daemon
+.SH "SYNOPSIS"
+\fByum-updatesd\fP
+.SH "DESCRIPTION"
+.PP 
+\fByum-updatesd\fP provides notification of updates which are
+available to be applied to your system.  This notification can be done
+either via syslog, email or over dbus.  Configuration is done via the
+\fByum-updatesd.conf(5) \fR file.  
+.PP 
+.SH "SEE ALSO"
+.nf
+.I yum(8)
+.I yum-updatesd.conf(5)
+http://linux.duke.edu/yum/
+.fi 
diff --git a/docs/yum-updatesd.conf.5 b/docs/yum-updatesd.conf.5
new file mode 100644
index 0000000..ee6e7aa
--- /dev/null
+++ b/docs/yum-updatesd.conf.5
@@ -0,0 +1,70 @@
+.TH "yum-updatesd.conf" "5" "" "Jeremy Katz" "yum-updatesd configuration file"
+.SH "NAME"
+.LP 
+\fByum-updatesd.conf\fR \- Configuration file for \fByum-updatesd(8)\fR.
+.SH "DESCRIPTION"
+.LP 
+yum-updatesd uses a configuration file at \fB/etc/yum/yum-updatesd.conf\fR. 
+.LP
+Additional configuration information is read from the main \fByum.conf
+(5)\fR configuration file.
+
+.SH "PARAMETERS"
+.LP
+There is one section in the yum-updatesd configuration file, main,
+which defines all of the global configuration options.
+
+.SH "[main] OPTIONS"
+.LP 
+The [main] section must exist for yum-updatesd to do anything. It
+consists of the following options:
+
+.IP \fBrun_interval\fR
+Number of seconds to wait between checks for available updates.
+
+.IP \fBupdaterefresh\fR
+Minimum number of seconds between update information refreshes to
+avoid hitting the server too often.
+
+.IP \fBemit_via\fR
+List of ways to emit update notification.  Valid values are `email',
+`dbus' and `syslog'.
+
+.IP \fBdo_update\fR
+Boolean option to decide whether or not updates should be
+automatically applied.  Defaults to False.
+
+.IP \fBdo_download_deps\fR
+Boolean option to decide whether or not updates should be
+automatically downloaded.  Defaults to False.
+
+.IP \fBdo_download_deps\fR
+Boolean option to automatically download dependencies of packages which need
+updating as well.  Defaults to False.
+
+.SH "MAIL OPTIONS"
+.IP \fBemail_to\fR
+List of email addresses to send update notification to.  Defaults to 
+`root at localhost'.
+
+.IP \fBemail_from\fR
+Email address for update notifications to be from.  Defaults to 
+`yum-updatesd at localhost'.
+
+.SH "SYSLOG OPTIONS"
+.IP \fBsyslog_facility\fR
+What syslog facility should be used.  Defaults to `DAEMON'.
+
+.IP \fBsyslog_level\fR
+Level of syslog messages.  Defaults to `WARN'.
+
+
+.SH "FILES"
+.nf 
+/etc/yum/yum-updatesd.conf
+
+.SH "SEE ALSO"
+.LP 
+yum-updatesd(8)
+yum.conf(5)
+
diff --git a/etc/Makefile b/etc/Makefile
index 7124035..1ba5a1f 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -12,3 +12,13 @@ install:
 
 	mkdir -p $(DESTDIR)/etc/logrotate.d
 	install -m 644 yum.logrotate $(DESTDIR)/etc/logrotate.d/yum
+
+	mkdir -p $(DESTDIR)/etc/rc.d/init.d
+	install -m 755 yum-updatesd.init $(DESTDIR)/etc/rc.d/init.d/yum-updatesd
+
+	mkdir -p $(DESTDIR)/etc/dbus-1/system.d/
+	install -m 755 yum-updatesd-dbus.conf $(DESTDIR)/etc/dbus-1/system.d/yum-updatesd.conf
+
+	install -m 755 yum-updatesd.conf $(DESTDIR)/etc/yum/yum-updatesd.conf
+
+
diff --git a/etc/yum-updatesd.init b/etc/yum-updatesd.init
new file mode 100755
index 0000000..55f166d
--- /dev/null
+++ b/etc/yum-updatesd.init
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# yum           This shell script enables the yum-updates daemon
+#
+# Author:       Jeremy Katz <katzj at redhat.com>
+#
+# chkconfig:	345 97 03
+#
+# description:  This is a daemon which periodically checks for updates \
+#               and can send notifications via mail, dbus or syslog.
+# processname:  yum-updatesd
+# config: /etc/yum/yum-updatesd.conf
+# pidfile: /var/run/yum-updatesd.pid
+#
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+RETVAL=0
+
+start() {
+	echo -n $"Starting yum-updatesd: "
+	daemon +19 yum-updatesd
+	RETVAL=$?
+	echo
+	[ $RETVAL -eq 0 ] && touch /var/lock/subsys/yum-updatesd
+}
+
+stop() {
+	echo -n $"Stopping yum-updatesd: "
+	killproc yum-updatesd
+	echo
+	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/yum-updatesd
+}
+
+restart() {
+	stop
+	start
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop) 
+	stop
+	;;
+  restart|force-reload|reload)
+	restart
+	;;
+  condrestart)
+	[ -f /var/lock/subsys/yum-updatesd ] && restart
+	;;
+  status)
+	status yum-updatesd
+	RETVAL=$?
+	;;
+  *)
+	echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
+	exit 1
+esac
+
+exit $RETVAL
diff --git a/yum-updatesd.py b/yum-updatesd.py
new file mode 100644
index 0000000..ac05f2b
--- /dev/null
+++ b/yum-updatesd.py
@@ -0,0 +1,654 @@
+#!/usr/bin/python -tt
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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.
+
+# (c)2006 Duke University, Red Hat, Inc.
+# Seth Vidal <skvidal at linux.duke.edu>
+# Jeremy Katz <katzj at redhat.com>
+
+#TODO:
+# - clean up config and work on man page for docs
+# - need to be able to cancel downloads.  requires some work in urlgrabber
+# - what to do if we're asked to exit while updates are being applied?
+# - what to do with the lock around downloads/updates
+
+# 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 gettext
+import os
+import sys
+import time
+import gzip
+import dbus
+import dbus.service
+import dbus.glib
+import gobject
+import smtplib
+import threading
+from optparse import OptionParser
+from email.MIMEText import MIMEText
+
+
+
+import yum
+import yum.Errors
+import syslog
+from yum.config import BaseConfig, Option, IntOption, ListOption, BoolOption
+from yum.parser import ConfigPreProcessor
+from ConfigParser import ConfigParser, ParsingError
+from yum.constants import *
+from yum.update_md import UpdateMetadata
+
+# FIXME: is it really sane to use this from here?
+sys.path.append('/usr/share/yum-cli')
+import callback
+
+config_file = '/etc/yum/yum-updatesd.conf'
+initial_directory = os.getcwd()
+
+class UpdateEmitter(object):
+    """Abstract object for implementing different types of emitters."""
+    def __init__(self):
+        pass
+    def updatesAvailable(self, updateInfo):
+        """Emitted when there are updates available to be installed.
+        If not doing the download here, then called immediately on finding
+        new updates.  If we do the download here, then called after the
+        updates have been downloaded."""
+        pass
+    def updatesDownloading(self, updateInfo):
+        """Emitted to give feedback of update download starting."""
+        pass
+    def updatesApplied(self, updateInfo):
+        """Emitted on successful installation of updates."""
+        pass
+    def updatesFailed(self, errmsgs):
+        """Emitted when an update has failed to install."""
+        pass
+    def checkFailed(self, error):
+        """Emitted when checking for updates failed."""
+        pass
+
+    def setupFailed(self, error, translation_domain):
+       """Emitted when plugin initialization failed."""
+       pass
+ 
+
+class SyslogUpdateEmitter(UpdateEmitter):
+    def __init__(self, syslog_facility, ident = "yum-updatesd",
+                 level = "WARN"):
+        UpdateEmitter.__init__(self)
+        syslog.openlog(ident, 0, self._facilityMap(syslog_facility))
+        self.level = level
+        
+    def updatesAvailable(self, updateInfo):
+        num = len(updateInfo)
+        level = self.level
+        if num > 1:
+            msg = "%d updates available" %(num,)
+        elif num == 1:
+            msg = "1 update available"
+        else:
+            msg = "No updates available"
+            level = syslog.LOG_DEBUG
+
+        syslog.syslog(self._levelMap(level), msg)
+
+    def _levelMap(self, lvl):
+        level_map = { "EMERG": syslog.LOG_EMERG,
+                      "ALERT": syslog.LOG_ALERT,
+                      "CRIT": syslog.LOG_CRIT,
+                      "ERR": syslog.LOG_ERR,
+                      "WARN": syslog.LOG_WARNING,
+                      "NOTICE": syslog.LOG_NOTICE,
+                      "INFO": syslog.LOG_INFO,
+                      "DEBUG": syslog.LOG_DEBUG }
+        if type(lvl) == int:
+            return lvl
+        if level_map.has_key(lvl.upper()):
+            return level_map[lvl.upper()]
+        return syslog.LOG_INFO
+
+    def _facilityMap(self, facility):
+        facility_map = { "KERN": syslog.LOG_KERN,
+                         "USER": syslog.LOG_USER,
+                         "MAIL": syslog.LOG_MAIL,
+                         "DAEMON": syslog.LOG_DAEMON,
+                         "AUTH": syslog.LOG_AUTH,
+                         "LPR": syslog.LOG_LPR,
+                         "NEWS": syslog.LOG_NEWS,
+                         "UUCP": syslog.LOG_UUCP,
+                         "CRON": syslog.LOG_CRON,
+                         "LOCAL0": syslog.LOG_LOCAL0,
+                         "LOCAL1": syslog.LOG_LOCAL1,
+                         "LOCAL2": syslog.LOG_LOCAL2,
+                         "LOCAL3": syslog.LOG_LOCAL3,
+                         "LOCAL4": syslog.LOG_LOCAL4,
+                         "LOCAL5": syslog.LOG_LOCAL5,
+                         "LOCAL6": syslog.LOG_LOCAL6,
+                         "LOCAL7": syslog.LOG_LOCAL7,}
+        if type(facility) == int:
+            return facility
+        elif facility_map.has_key(facility.upper()):
+            return facility_map[facility.upper()]
+        return syslog.LOG_DAEMON
+
+
+class EmailUpdateEmitter(UpdateEmitter):
+    def __init__(self, sender, rcpt):
+        UpdateEmitter.__init__(self)        
+        self.sender = sender
+        self.rcpt = rcpt
+
+    def updatesAvailable(self, updateInfo):
+        num = len(updateInfo)
+        if num < 1:
+            return
+
+        output = """
+        Hi,
+        There are %d package updates available. Please run the system
+        updater.
+        
+        Thank You,
+        Your Computer
+        """ % num
+                
+        msg = MIMEText(output)
+        msg['Subject'] = "%d Updates Available" %(num,)
+        msg['From'] = self.sender
+        msg['To'] = ",".join(self.rcpt)
+        s = smtplib.SMTP()
+        s.connect()
+        s.sendmail(self.sender, self.rcpt, msg.as_string())
+        s.close()
+
+class DbusUpdateEmitter(UpdateEmitter):
+    def __init__(self):
+        UpdateEmitter.__init__(self)        
+        bus = dbus.SystemBus()
+        name = dbus.service.BusName('edu.duke.linux.yum', bus = bus)
+        yum_dbus = YumDbusInterface(name)
+        self.dbusintf = yum_dbus
+
+    def updatesAvailable(self, updateInfo):
+        num = len(updateInfo)
+        msg = "%d" %(num,)
+        if num > 0:
+            self.dbusintf.UpdatesAvailableSignal(msg)
+        else:
+            self.dbusintf.NoUpdatesAvailableSignal(msg)
+
+    def updatesFailed(self, errmsgs):
+        self.dbusintf.UpdatesFailedSignal(errmsgs)
+
+    def updatesApplied(self, updinfo):
+        self.dbusintf.UpdatesAppliedSignal(updinfo)
+
+    def checkFailed(self, error):
+        self.dbusintf.CheckFailedSignal(error)
+
+    def setupFailed(self, error, translation_domain):
+        self.dbusintf.SetupFailedSignal(error, translation_domain)
+
+
+class YumDbusInterface(dbus.service.Object):
+    def __init__(self, bus_name, object_path='/UpdatesAvail'):
+        dbus.service.Object.__init__(self, bus_name, object_path)
+
+    @dbus.service.signal('edu.duke.linux.yum')
+    def UpdatesAvailableSignal(self, message):
+        pass
+
+    @dbus.service.signal('edu.duke.linux.yum')
+    def NoUpdatesAvailableSignal(self, message):
+        pass
+        
+    @dbus.service.signal('edu.duke.linux.yum')
+    def UpdatesFailedSignal(self, errmsgs):
+        pass
+
+    @dbus.service.signal('edu.duke.linux.yum')
+    def UpdatesAppliedSignal(self, updinfo):
+        pass
+
+    @dbus.service.signal('edu.duke.linux.yum')
+    def CheckFailedSignal(self, message):
+        pass
+
+    @dbus.service.signal('edu.duke.linux.yum')
+    def SetupFailedSignal(self, message, translation_domain=""):
+        pass
+
+
+class UDConfig(BaseConfig):
+    """Config format for the daemon"""
+    run_interval = IntOption(3600)
+    nonroot_workdir = Option("/var/tmp/yum-updatesd")
+    emit_via = ListOption(['dbus', 'email', 'syslog'])
+    email_to = ListOption(["root"])
+    email_from = Option("root")
+    dbus_listener = BoolOption(True)
+    do_update = BoolOption(False)
+    do_download = BoolOption(False)
+    do_download_deps = BoolOption(False)
+    updaterefresh = IntOption(3600)
+    syslog_facility = Option("DAEMON")
+    syslog_level = Option("WARN")
+    syslog_ident = Option("yum-updatesd")
+    yum_config = Option("/etc/yum/yum.conf")
+
+
+class UpdateBuildTransactionThread(threading.Thread):
+    def __init__(self, updd, name):
+        self.updd = updd
+        threading.Thread.__init__(self, name=name)
+
+    def run(self):
+        self.updd.tsInfo.makelists()
+        try:
+            (result, msgs) = self.updd.buildTransaction()
+        except yum.Errors.RepoError, errmsg: # error downloading hdrs
+            msgs = ["Error downloading headers"]
+            self.updd.emitUpdateFailed(msgs)
+            return
+
+        dlpkgs = map(lambda x: x.po, filter(lambda txmbr:
+                                            txmbr.ts_state in ("i", "u"),
+                                            self.updd.tsInfo.getMembers()))
+        self.updd.downloadPkgs(dlpkgs)
+        self.processPkgs(dlpkgs)
+
+
+class UpdateDownloadThread(UpdateBuildTransactionThread):
+    def __init__(self, updd):
+        UpdateBuildTransactionThread.__init__(self, updd,
+            name="UpdateDownloadThread")
+
+    def processPkgs(self, dlpkgs):
+        self.updd.emitAvailable()
+        self.updd.releaseLocks()
+
+
+class UpdateInstallThread(UpdateBuildTransactionThread):
+    def __init__(self, updd):
+        UpdateBuildTransactionThread.__init__(self, updd,
+            name="UpdateInstallThread")
+
+    def failed(self, msgs):
+        self.updd.emitUpdateFailed(msgs)
+        self.updd.releaseLocks()
+
+    def success(self):
+        self.updd.emitUpdateApplied()
+        self.updd.releaseLocks()
+
+        self.updd.updateInfo = None
+        self.updd.updateInfoTime = None        
+        
+    def processPkgs(self, dlpkgs):
+        for po in dlpkgs:
+            result, err = self.updd.sigCheckPkg(po)
+            if result == 0:
+                continue
+            elif result == 1:
+                try:
+                    self.updd.getKeyForPackage(po)
+                except yum.Errors.YumBaseError, errmsg:
+                    self.failed([str(errmsg)])
+
+        del self.updd.ts
+        self.updd.initActionTs() # make a new, blank ts to populate
+        self.updd.populateTs(keepold=0)
+        self.updd.ts.check() #required for ordering
+        self.updd.ts.order() # order
+        cb = callback.RPMInstallCallback(output = 0)
+        cb.filelog = True
+            
+        cb.tsInfo = self.updd.tsInfo
+        try:
+            self.updd.runTransaction(cb=cb)
+        except yum.Errors.YumBaseError, err:
+            self.failed([str(err)])
+
+        self.success()
+
+class UpdatesDaemon(yum.YumBase):
+    def __init__(self, opts):
+        yum.YumBase.__init__(self)
+        self.opts = opts
+        self.didSetup = False
+
+        self.emitters = []
+        if 'dbus' in self.opts.emit_via:
+            self.emitters.append(DbusUpdateEmitter())
+        if 'email' in self.opts.emit_via:
+            self.emitters.append(EmailUpdateEmitter(self.opts.email_from,
+                                                    self.opts.email_to))
+        if 'syslog' in self.opts.emit_via:
+            self.emitters.append(SyslogUpdateEmitter(self.opts.syslog_facility,
+                                                     self.opts.syslog_ident,
+                                                     self.opts.syslog_level))
+
+        self.updateInfo = []
+        self.updateInfoTime = None
+
+    def doSetup(self):
+        # if we are not root do the special subdir thing
+        if os.geteuid() != 0:
+            if not os.path.exists(self.opts.nonroot_workdir):
+                os.makedirs(self.opts.nonroot_workdir)
+            self.repos.setCacheDir(self.opts.nonroot_workdir)
+
+        self.doConfigSetup(fn=self.opts.yum_config)
+
+    def refreshUpdates(self):
+        self.doLock()
+        try:
+            self.doRepoSetup()
+            self.doSackSetup()
+            self.updateCheckSetup()
+        except Exception, e:
+            syslog.syslog(syslog.LOG_WARNING,
+                          "error getting update info: %s" %(e,))
+            self.emitCheckFailed("%s" %(e,))
+            self.doUnlock()
+            return False
+        return True
+
+    def populateUpdateMetadata(self):
+        self.updateMetadata = UpdateMetadata()
+        repos = []
+
+        for (new, old) in self.up.getUpdatesTuples():
+            pkg = self.getPackageObject(new)
+            if pkg.repoid not in repos:
+                repo = self.repos.getRepo(pkg.repoid)
+                repos.append(repo.id)
+                try: # grab the updateinfo.xml.gz from the repodata
+                    md = repo.retrieveMD('updateinfo')
+                except Exception: # can't find any; silently move on
+                    continue
+                md = gzip.open(md)
+                self.updateMetadata.add(md)
+                md.close()
+
+    def populateUpdates(self):
+        def getDbusPackageDict(pkg):
+            """Returns a dictionary corresponding to the package object
+            in the form that we can send over the wire for dbus."""
+            pkgDict = {
+                    "name": pkg.name,
+                    "version": pkg.version,
+                    "release": pkg.release,
+                    "epoch": pkg.epoch,
+                    "arch": pkg.arch,
+                    "sourcerpm": pkg.sourcerpm,
+                    "summary": pkg.summary or "",
+            }
+
+            # check if any updateinfo is available
+            md = self.updateMetadata.get_notice((pkg.name, pkg.ver, pkg.rel))
+            if md:
+                # right now we only want to know if it is a security update
+                pkgDict['type'] = md['type']
+
+            return pkgDict
+
+        if self.up is None:
+            # we're _only_ called after updates are setup
+            return
+
+        self.populateUpdateMetadata()
+
+        self.updateInfo = []
+        for (new, old) in self.up.getUpdatesTuples():
+            n = getDbusPackageDict(self.getPackageObject(new))
+            o = getDbusPackageDict(self.rpmdb.searchPkgTuple(old)[0])
+            self.updateInfo.append((n, o))
+
+        if self.conf.obsoletes:
+            for (obs, inst) in self.up.getObsoletesTuples():
+                n = getDbusPackageDict(self.getPackageObject(obs))
+                o = getDbusPackageDict(self.rpmdb.searchPkgTuple(inst)[0])
+                self.updateInfo.append((n, o))
+
+        self.updateInfoTime = time.time()
+
+    def populateTsInfo(self):
+        # figure out the updates
+        for (new, old) in self.up.getUpdatesTuples():
+            updating = self.getPackageObject(new)
+            updated = self.rpmdb.searchPkgTuple(old)[0]
+                
+            self.tsInfo.addUpdate(updating, updated)
+
+        # and the obsoletes
+        if self.conf.obsoletes:
+            for (obs, inst) in self.up.getObsoletesTuples():
+                obsoleting = self.getPackageObject(obs)
+                installed = self.rpmdb.searchPkgTuple(inst)[0]
+                
+                self.tsInfo.addObsoleting(obsoleting, installed)
+                self.tsInfo.addObsoleted(installed, obsoleting)
+
+    def updatesCheck(self):
+        if not self.didSetup:
+            try:
+                self.doSetup()
+            except Exception, e:
+                syslog.syslog(syslog.LOG_WARNING,
+                              "error initializing: %s" % e)
+
+                if isinstance(e, yum.plugins.PluginYumExit):
+                    self.emitSetupFailed(e.value, e.translation_domain)
+                else:
+                    # if we don't know where the string is from, then assume
+                    # it's not marked for translation (versus sending 
+                    # gettext.textdomain() and assuming it's from the default
+                    # domain for this app)
+                    self.emitSetupFailed(str(e))
+                # Setup failed, let's restart and try again after the update
+                # interval
+                restart()
+            else:
+                self.didSetup = True
+
+        try:
+            if not self.refreshUpdates():
+                return
+        except yum.Errors.LockError:
+            return True # just pass for now
+
+        try:
+            self.populateTsInfo()
+            self.populateUpdates()
+
+            if self.opts.do_update:
+                uit = UpdateInstallThread(self)
+                uit.start()
+            elif self.opts.do_download:
+                self.emitDownloading()
+                dl = UpdateDownloadThread(self)
+                dl.start()
+            else:
+                # just notify about things being available
+                self.emitAvailable()
+                self.releaseLocks()
+        except Exception, e:
+            self.emitCheckFailed("%s" %(e,))
+            self.doUnlock()
+
+        return True
+
+    def getUpdateInfo(self):
+        # if we have a cached copy, use it
+        if self.updateInfoTime and (time.time() - self.updateInfoTime <
+                                    self.opts.updaterefresh):
+            return self.updateInfo
+            
+        # try to get the lock so we can update the info.  fall back to
+        # cached if available or try a few times.
+        for i in range(10):
+            try:
+                self.doLock()
+                break
+            except yum.Errors.LockError:
+                # if we can't get the lock, return what we have if we can
+                if self.updateInfo:
+                    return self.updateInfo
+                time.sleep(1)
+        else:
+            return []
+
+        try:
+            self.updateCheckSetup()
+
+            self.populateUpdates()
+
+            self.releaseLocks()
+        except:
+            self.doUnlock()
+
+        return self.updateInfo
+
+    def updateCheckSetup(self):
+        self.doTsSetup()
+        self.doRpmDBSetup()
+        self.doUpdateSetup()
+
+    def releaseLocks(self):
+        self.closeRpmDB()
+        self.doUnlock()
+
+    def emitAvailable(self):
+        """method to emit a notice about updates"""
+        map(lambda x: x.updatesAvailable(self.updateInfo), self.emitters)
+
+    def emitDownloading(self):
+        """method to emit a notice about updates downloading"""
+        map(lambda x: x.updatesDownloading(self.updateInfo), self.emitters)
+
+    def emitUpdateApplied(self):
+        """method to emit a notice when automatic updates applied"""
+        map(lambda x: x.updatesApplied(self.updateInfo), self.emitters)
+
+    def emitUpdateFailed(self, errmsgs):
+        """method to emit a notice when automatic updates failed"""
+        map(lambda x: x.updatesFailed(errmsgs), self.emitters)
+
+    def emitCheckFailed(self, error):
+        """method to emit a notice when checking for updates failed"""
+        map(lambda x: x.checkFailed(error), self.emitters)
+
+    def emitSetupFailed(self, error, translation_domain=""):
+        """method to emit a notice when checking for updates failed"""
+        map(lambda x: x.setupFailed(error, translation_domain), self.emitters)
+
+
+class YumDbusListener(dbus.service.Object):
+    def __init__(self, updd, bus_name, object_path='/Updatesd',
+                 allowshutdown = False):
+        dbus.service.Object.__init__(self, bus_name, object_path)
+        self.updd = updd
+        self.allowshutdown = allowshutdown
+
+    def doCheck(self):
+        self.updd.updatesCheck()
+        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(shutDown)
+        return True
+
+    @dbus.service.method("edu.duke.linux.yum", in_signature="", out_signature="a(a{ss}a{ss})")
+    def GetUpdateInfo(self):
+        # FIXME: should this be async?
+        upds = self.updd.getUpdateInfo()
+        return upds
+        
+
+def shutDown():
+    sys.exit(0)
+
+def restart():
+    os.chdir(initial_directory)
+    os.execve(sys.argv[0], sys.argv, os.environ)  
+
+def main(options = None):
+    # we'll be threading for downloads/updates
+    gobject.threads_init()
+    dbus.glib.threads_init()
+
+    if options is None:
+        parser = OptionParser()
+        parser.add_option("-f", "--no-fork", action="store_true", default=False, dest="nofork")
+        parser.add_option("-r", "--remote-shutdown", action="store_true", default=False, dest="remoteshutdown")    
+        (options, args) = parser.parse_args()
+
+        if not options.nofork:
+            if os.fork():
+                sys.exit()
+            fd = os.open("/dev/null", os.O_RDWR)
+            os.dup2(fd, 0)
+            os.dup2(fd, 1)
+            os.dup2(fd, 2)
+            os.close(fd)
+
+    confparser = ConfigParser()
+    opts = UDConfig()
+    
+    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)
+
+    syslog.openlog("yum-updatesd", 0, syslog.LOG_DAEMON)
+
+    opts.populate(confparser, 'main')
+    updd = UpdatesDaemon(opts)
+
+    if opts.dbus_listener:
+        bus = dbus.SystemBus()
+        name = dbus.service.BusName("edu.duke.linux.yum", bus=bus)
+        YumDbusListener(updd, name, allowshutdown = options.remoteshutdown)
+    
+    run_interval_ms = opts.run_interval * 1000 # needs to be in ms
+    gobject.timeout_add(run_interval_ms, updd.updatesCheck)
+
+    mainloop = gobject.MainLoop()
+    mainloop.run()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/yum.spec b/yum.spec
index c5406f8..f65248e 100644
--- a/yum.spec
+++ b/yum.spec
@@ -1,7 +1,7 @@
 Summary: RPM installer/updater
 Name: yum
 Version: 3.2.5
-Release: 2
+Release: 1
 License: GPL
 Group: System Environment/Base
 Source: %{name}-%{version}.tar.gz
@@ -25,6 +25,19 @@ Yum is a utility that can check for and automatically download and
 install updated RPM packages. Dependencies are obtained and downloaded 
 automatically prompting the user as necessary.
 
+%package updatesd
+Summary: Update notification daemon
+Group: Applications/System
+Requires: yum = %{version}-%{release}
+Requires: dbus-python
+Requires: pygobject2
+Prereq: /sbin/chkconfig 
+Prereq: /sbin/service
+
+%description updatesd
+yum-updatesd provides a daemon which checks for available updates and 
+can notify you when they are available via email, syslog or dbus. 
+
 %prep
 %setup -q
 
@@ -42,6 +55,18 @@ make DESTDIR=$RPM_BUILD_ROOT install
 [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
 
 
+%post updatesd
+/sbin/chkconfig --add yum-updatesd
+/sbin/service yum-updatesd condrestart >/dev/null 2>&1
+exit 0
+
+%preun updatesd
+if [ $1 = 0 ]; then
+ /sbin/chkconfig --del yum-updatesd
+ /sbin/service yum-updatesd stop >/dev/null 2>&1
+fi
+exit 0
+
 %files
 %defattr(-, root, root)
 %doc README AUTHORS COPYING TODO INSTALL ChangeLog PLUGINS
@@ -58,11 +83,15 @@ make DESTDIR=$RPM_BUILD_ROOT install
 %{_mandir}/man*/yum.*
 %{_mandir}/man*/yum-shell*
 
+%files updatesd
+%defattr(-, root, root)
+%config(noreplace) %{_sysconfdir}/yum/yum-updatesd.conf
+%config %{_sysconfdir}/rc.d/init.d/yum-updatesd
+%config %{_sysconfdir}/dbus-1/system.d/yum-updatesd.conf
+%{_sbindir}/yum-updatesd
+%{_mandir}/man*/yum-updatesd*
 
 %changelog
-* Sun Sep 30 2007 James Bowes <jbowes at redhat.com> 3.2.5-2
-- remove yum-updatesd (it's external now)
-
 * Mon Sep 10 2007 Seth Vidal <skvidal at fedoraproject.org>
 - 3.2.5
 



More information about the Yum-cvs-commits mailing list