[yum-commits] 6 commits - docs/Makefile docs/yum-cron.8 etc/Makefile etc/yum-cron.conf yum-cron/cleanup.yum yum-cron/Makefile yum-cron/update.yum yum-cron/yum-cleanup.cron.sh yum-cron/yum-cron.py yum-cron/yum-cron.sh yum-cron/yum-cron.sysconfig yum-cron/yum-cron.sysvinit yum-cron/yum-update.cron.sh yum.spec
zpavlas at osuosl.org
zpavlas at osuosl.org
Thu Oct 25 15:24:03 UTC 2012
docs/Makefile | 1
docs/yum-cron.8 | 49 +
etc/Makefile | 5
etc/yum-cron.conf | 55 ++
yum-cron/Makefile | 8
yum-cron/cleanup.yum | 4
yum-cron/update.yum | 3
yum-cron/yum-cleanup.cron.sh | 30 -
yum-cron/yum-cron.py | 1113 +++++++++++++++++++++++++++++++++++++++++++
yum-cron/yum-cron.sh | 170 ------
yum-cron/yum-cron.sysconfig | 92 ---
yum-cron/yum-cron.sysvinit | 7
yum-cron/yum-update.cron.sh | 26 -
yum.spec | 9
14 files changed, 1227 insertions(+), 345 deletions(-)
New commits:
commit 64f26faced403af46a8df135af3623bcea205600
Author: ZdenÄk Pavlas <zpavlas at redhat.com>
Date: Thu Oct 25 16:21:58 2012 +0200
Enable background downloading
When not applying the updates, turn on background downloading.
Send messages that download was succesfull in sys.exit(0) path.
diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
index 05c87fb..44fc785 100755
--- a/yum-cron/yum-cron.py
+++ b/yum-cron/yum-cron.py
@@ -932,16 +932,17 @@ class YumCronBase(yum.YumBase):
self.tsInfo.getMembers()))
try:
# Download the updates
+ self.conf.download_only = not self.opts.apply_updates
self.downloadPkgs(dlpkgs)
except Exception, e:
self.emitDownloadFailed("%s" % e)
sys.exit(1)
- else :
- # Emit a message that the packages have been downloaded
- # successfully
- if emit :
+ except SystemExit, e:
+ if e.code == 0:
+ # Emit a message that the packages have been downloaded
self.emitDownloaded()
self.emitMessages()
+ raise
def installUpdates(self, emit):
"""Apply the available updates.
commit f67f8bae744a799be52ac934e7123715017dd266
Author: ZdenÄk Pavlas <zpavlas at redhat.com>
Date: Thu Oct 25 15:06:34 2012 +0200
minor fixes, some cleanup
- fix locking issue when running yum-cron as non-root user
- drop nonroot_workdir option, use default userdir logic
- update default paths, fix some typos
diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
index d89786d..05c87fb 100755
--- a/yum-cron/yum-cron.py
+++ b/yum-cron/yum-cron.py
@@ -1,7 +1,6 @@
#!/usr/bin/python -tt
import os
import sys
-import time
import gzip
from socket import gethostname
@@ -23,7 +22,7 @@ from time import sleep
sys.path.append('/usr/share/yum-cli')
import callback
-default_config_file = '/home/nick/yum/new-yum-cron/yum-cron.conf'
+default_config_file = '/etc/yum/yum-cron.conf'
class UpdateEmitter(object):
"""Abstract class for implementing different types of emitters.
@@ -648,7 +647,7 @@ class EmailEmitter(UpdateEmitter):
:param errmsgs: a string that contains the error message
"""
self.subject = "Yum: Failed to download updates on %s" % self.opts.system_name
- super(EmailEmitter, self).lockFailed(errmsg)
+ super(EmailEmitter, self).downloadFailed(errmsg)
def updatesFailed(self, errmsg):
"""Append a message to the output list stating that installing
@@ -694,7 +693,6 @@ class YumCronConfig(BaseConfig):
"""Class to parse configuration information from the config file, and
to store this information.
"""
- nonroot_workdir = Option("/var/tmp/yum-cron")
system_name = Option(gethostname())
output_width = IntOption(80)
random_sleep = IntOption(0)
@@ -795,9 +793,7 @@ class YumCronBase(yum.YumBase):
# 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.setCacheDir()
# Create the configuration
self.conf
commit c857fe178b431ed378ff7e48d031374f476843c2
Author: ZdenÄk Pavlas <zpavlas at redhat.com>
Date: Thu Oct 25 15:01:06 2012 +0200
0yum-update.cron is not a config file
diff --git a/yum.spec b/yum.spec
index 728071f..a309594 100644
--- a/yum.spec
+++ b/yum.spec
@@ -281,7 +281,7 @@ exit 0
%files cron
%defattr(-,root,root)
%doc COPYING
-%config(noreplace) %{_sysconfdir}/cron.daily/0yum-update.cron
+%{_sysconfdir}/cron.daily/0yum-update.cron
%config(noreplace) %{_sysconfdir}/yum/yum-cron.conf
%{_sysconfdir}/rc.d/init.d/yum-cron
%{_sbindir}/yum-cron
commit 52eaca4291678aad3c7499eddea60c1b89134359
Author: ZdenÄk Pavlas <zpavlas at redhat.com>
Date: Thu Oct 25 14:56:42 2012 +0200
yum-updatesd.conf is not executable
diff --git a/etc/Makefile b/etc/Makefile
index 0c7b9f0..ec5af83 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -24,9 +24,7 @@ install:
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
-
+ install -m 644 yum-updatesd.conf $(YUMETC)
mkdir -p $(DESTDIR)/etc/bash_completion.d
install -m 644 yum.bash $(DESTDIR)/etc/bash_completion.d
install -m 644 yum-cron.conf $(YUMETC)
commit 82f2f1b6c6315f1636d2453bf4ac1d77d0c5e53f
Author: ZdenÄk Pavlas <zpavlas at redhat.com>
Date: Thu Oct 25 14:45:36 2012 +0200
yum-cron: add new yum-cron.py
- add yum-cron.8, yum-cron.conf
- yum-cron.py replaces yum-cron.sh
diff --git a/docs/Makefile b/docs/Makefile
index dc31d0e..8cc7f9f 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -13,3 +13,4 @@ install:
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
+ install -m 644 yum-cron.8 $(DESTDIR)/usr/share/man/man8
diff --git a/docs/yum-cron.8 b/docs/yum-cron.8
new file mode 100644
index 0000000..4d01a5a
--- /dev/null
+++ b/docs/yum-cron.8
@@ -0,0 +1,49 @@
+.\" yum-cron - cron interface for yum
+.TH "yum-cron" "8" "" "Nick Jacek" ""
+.SH "NAME"
+yum-cron \- an interface to convieniently call yum from cron
+
+.SH "SYNOPSIS"
+\fByum-cron\fP [config-file]
+
+.SH "DESCRIPTION"
+.PP
+\fByum-cron\fP is an alternate interface to yum that is optimised to
+be convenient to call from cron. It provides methods to keep
+repository metadata up to date, and to check for, download, and apply
+updates. Rather than accepting many different command line arguments,
+the different functions of yum-cron can be accessed through config
+files.
+.PP
+\fIconfig-file\fP is used to optionally specify the path to the
+configuration file to use. If it is not given, the default
+configuration file will be used. It is useful to be able to specify
+different configuration files for different use cases. For example,
+one configuration file might be set to update the repository metadata,
+and a line could be added to the crontab to run yum-cron frequently
+using this file. Then, another configuration file might be set to
+install updates, and yum-cron could be run from cron using this file
+just once each day.
+
+.SH "FILES"
+.nf
+/etc/yum-cron.conf
+.fi
+
+.PP
+.SH "SEE ALSO"
+.nf
+.I yum (8)
+.fi
+
+.PP
+.SH "AUTHORS"
+.nf
+See the Authors file included with this program.
+.fi
+
+.PP
+.SH "BUGS"
+There of course aren't any bugs, but if you find any, you should email
+ the mailing list, yum at lists.baseurl.org, or consult bugzilla.
+.fi
diff --git a/etc/Makefile b/etc/Makefile
index a512cdf..0c7b9f0 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -29,3 +29,4 @@ install:
mkdir -p $(DESTDIR)/etc/bash_completion.d
install -m 644 yum.bash $(DESTDIR)/etc/bash_completion.d
+ install -m 644 yum-cron.conf $(YUMETC)
diff --git a/etc/yum-cron.conf b/etc/yum-cron.conf
new file mode 100644
index 0000000..269c6b7
--- /dev/null
+++ b/etc/yum-cron.conf
@@ -0,0 +1,55 @@
+[commands]
+# Whether a message should emitted when updates are available.
+update_messages = yes
+
+# Whether updates should be downloaded when they are available. Note
+# that updates_messages must also be yes for updates to be downloaded.
+download_updates = no
+
+# Whether updates should be applied when they are available. Note
+# that both update_messages and download_updates must also be yes for
+# the update to be applied
+apply_updates = no
+
+# Maximum amout of time to randomly sleep, in minutes. The program
+# will sleep for a random amount of time between 0 and random_sleep
+# minutes before running. This is useful for e.g. staggering the
+# times that multiple systems will access update servers. If
+# random_sleep is 0 or negative, the program will run immediately.
+random_sleep = 0
+
+
+[emitters]
+# Name to use for this system in messages that are emitted. If
+# system_name is None, the hostname will be used.
+system_name = None
+
+# How to send messages. Valid options are stdio and email. If
+# emit_via includes stdio, messages will be sent to stdout; this is useful
+# to have cron send the messages. If emit_via includes email, this
+# program will send email itself according to the configured options.
+# If emit_via is None or left blank, no messages will be sent.
+emit_via = stdio
+
+# The width, in characters, that messages that are emitted should be
+# formatted to.
+ouput_width = 80
+
+
+[email]
+# The address to send email messages from.
+email_from = root
+
+# List of addresses to send messages to.
+email_to = root
+
+# Name of the host to connect to to send email messages.
+email_host = localhost
+
+
+[groups]
+# List of groups to update
+group_list = None
+
+# The types of group packages to install
+group_package_types = mandatory, default
diff --git a/yum-cron/Makefile b/yum-cron/Makefile
index 4c26969..1a68a3c 100644
--- a/yum-cron/Makefile
+++ b/yum-cron/Makefile
@@ -12,4 +12,4 @@ install:
# manpage update, mlocate, and prelink
install -D -m 755 yum-update.cron.sh $(DESTDIR)/etc/cron.daily/0yum-update.cron
install -D -m 755 yum-cron.sysvinit $(DESTDIR)/etc/rc.d/init.d/yum-cron
- install -D -m 755 yum-cron.sh $(DESTDIR)/usr/sbin/yum-cron
+ install -D -m 755 yum-cron.py $(DESTDIR)/usr/sbin/yum-cron
diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
new file mode 100755
index 0000000..d89786d
--- /dev/null
+++ b/yum-cron/yum-cron.py
@@ -0,0 +1,1116 @@
+#!/usr/bin/python -tt
+import os
+import sys
+import time
+import gzip
+from socket import gethostname
+
+import yum
+import yum.Errors
+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
+from email.mime.text import MIMEText
+from yum.i18n import to_str, to_utf8, to_unicode, utf8_width, utf8_width_fill, utf8_text_fill
+from yum import _, P_
+import smtplib
+from random import random
+from time import sleep
+
+# FIXME: is it really sane to use this from here?
+sys.path.append('/usr/share/yum-cli')
+import callback
+
+default_config_file = '/home/nick/yum/new-yum-cron/yum-cron.conf'
+
+class UpdateEmitter(object):
+ """Abstract class for implementing different types of emitters.
+ Most methods will add certain messages the output list. Then,
+ the sendMessage method can be overridden in a subclass to
+ combine these messages and transmit them as required.
+ """
+
+ def __init__(self, opts):
+ self.opts = opts
+ self.output = []
+
+ def updatesAvailable(self, tsInfo):
+ """Appends a message to the output list stating that there are
+ updates available.
+
+ :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
+ instance that contains information about the transaction.
+ """
+ self.output.append('The following updates are available on %s:' % self.opts.system_name)
+ self.output.append(self._formatTransaction(tsInfo))
+
+ def updatesDownloading(self, tsInfo):
+ """Append a message to the output list stating that
+ downloading updates has started.
+
+ :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
+ instance that contains information about the transaction.
+ """
+ self.output.append('The following updates will be downloaded on %s:' % self.opts.system_name)
+ self.output.append(self._formatTransaction(tsInfo))
+
+ def updatesDownloaded(self):
+ """Append a message to the output list stating that updates
+ have been downloaded successfully.
+ """
+ self.output.append("Updates downloaded successfully.")
+
+ def updatesInstalling(self, tsInfo):
+ """Append a message to the output list stating that
+ installing updates has started.
+
+ :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
+ instance that contains information about the transaction.
+ """
+ self.output.append('The following updates will be applied on %s:' % self.opts.system_name)
+ self.output.append(self._formatTransaction(tsInfo))
+
+ def updatesInstalled(self):
+ """Append a message to the output list stating that updates
+ have been installed successfully.
+ """
+ self.output.append('The updates were sucessfully applied')
+
+ def setupFailed(self, errmsg):
+ """Append a message to the output list stating that setup
+ failed, and then call sendMessages to emit the output.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.output.append("Plugins failed to initialize with the following error message: \n%s"
+ % errmsg)
+ self.sendMessages()
+
+ def lockFailed(self, errmsg):
+ """Append a message to the output list stating that the
+ program failed to acquire the yum lock, then call sendMessages
+ to emit the output.
+
+ :param errmsg: a string that contains the error message
+ """
+ self.output.append("Failed to acquire the yum lock with the following error message: \n%s"
+ % errmsg)
+ self.sendMessages()
+
+ def checkFailed(self, errmsg):
+ """Append a message to the output stating that checking for
+ updates failed, then call sendMessages to emit the output.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.output.append("Failed to check for updates with the following error message: \n%s"
+ % errmsg)
+ self.sendMessages()
+
+ def groupError(self, errmsg):
+ """Append a message to the output list stating that an error
+ was encountered while checking for group updates.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.output.append("Error checking for group updates: \n%s"
+ % errmsg)
+
+ def groupFailed(self, errmsg):
+ """Append a message to the output list stating that checking
+ for group updates failed, then call sendMessages to emit the output.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.output.append("Failed to check for updates with the following error message: \n%s"
+ % errmsg)
+ self.sendMessages()
+
+ def downloadFailed(self, errmsg):
+ """Append a message to the output list stating that
+ downloading updates failed, then call sendMessages to emit the output.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.output.append("Updates failed to download with the following error message: \n%s"
+ % errmsg)
+ self.sendMessages()
+
+ def updatesFailed(self, errmsg):
+ """Append a message to the output list stating that installing
+ updates failed, then call sendMessages to emit the output.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.output.append("Updates failed to install with the following error message: \n%s"
+ % errmsg)
+ self.sendMessages()
+
+ def sendMessages(self):
+ """Send the messages that have been stored. This should be
+ overridden by inheriting classes to emit the messages
+ according to their individual methods.
+ """
+ pass
+
+ def _format_number(self, number, SI=0, space=' '):
+ """Return a human-readable metric-like string representation
+ of a number.
+
+ :param number: the number to be converted to a human-readable form
+ :param SI: If is 0, this function will use the convention
+ that 1 kilobyte = 1024 bytes, otherwise, the convention
+ that 1 kilobyte = 1000 bytes will be used
+ :param space: string that will be placed between the number
+ and the SI prefix
+ :return: a human-readable metric-like string representation of
+ *number*
+ """
+ symbols = [ ' ', # (none)
+ 'k', # kilo
+ 'M', # mega
+ 'G', # giga
+ 'T', # tera
+ 'P', # peta
+ 'E', # exa
+ 'Z', # zetta
+ 'Y'] # yotta
+
+ if SI: step = 1000.0
+ else: step = 1024.0
+
+ thresh = 999
+ depth = 0
+ max_depth = len(symbols) - 1
+
+ # we want numbers between 0 and thresh, but don't exceed the length
+ # of our list. In that event, the formatting will be screwed up,
+ # but it'll still show the right number.
+ while number > thresh and depth < max_depth:
+ depth = depth + 1
+ number = number / step
+
+ if type(number) == type(1) or type(number) == type(1L):
+ format = '%i%s%s'
+ elif number < 9.95:
+ # must use 9.95 for proper sizing. For example, 9.99 will be
+ # rounded to 10.0 with the .1f format string (which is too long)
+ format = '%.1f%s%s'
+ else:
+ format = '%.0f%s%s'
+
+ return(format % (float(number or 0), space, symbols[depth]))
+
+ def _fmtColumns(self, columns, msg=u'', end=u'', text_width=utf8_width):
+ """Return a row of data formatted into a string for output.
+ Items can overflow their columns.
+
+ :param columns: a list of tuples containing the data to
+ output. Each tuple contains first the item to be output,
+ then the amount of space allocated for the column, and then
+ optionally a type of highlighting for the item
+ :param msg: a string to begin the line of output with
+ :param end: a string to end the line of output with
+ :param text_width: a function to find the width of the items
+ in the columns. This defaults to utf8 but can be changed
+ to len() if you know it'll be fine
+ :return: a row of data formatted into a string for output
+ """
+ total_width = len(msg)
+ data = []
+ for col_data in columns[:-1]:
+ (val, width) = col_data
+
+ if not width: # Don't count this column, invisible text
+ msg += u"%s"
+ data.append(val)
+ continue
+
+ (align, width) = self._fmt_column_align_width(width)
+ val_width = text_width(val)
+ if val_width <= width:
+ # Don't use utf8_width_fill() because it sucks performance
+ # wise for 1,000s of rows. Also allows us to use len(), when
+ # we can.
+ msg += u"%s%s "
+ if (align == u'-'):
+ data.extend([val, " " * (width - val_width)])
+ else:
+ data.extend([" " * (width - val_width), val])
+ else:
+ msg += u"%s\n" + " " * (total_width + width + 1)
+ data.append(val)
+ total_width += width
+ total_width += 1
+ (val, width) = columns[-1]
+ (align, width) = self._fmt_column_align_width(width)
+ val = utf8_width_fill(val, width, left=(align == u'-'))
+ msg += u"%%s%s" % end
+ data.append(val)
+ return msg % tuple(data)
+
+ def _calcColumns(self, data, total_width, columns=None, remainder_column=0, indent=''):
+ """Dynamically calculate the widths of the columns that the
+ fields in data should be placed into for output.
+
+ :param data: a list of dictionaries that represent the data to
+ be output. Each dictionary in the list corresponds to annn
+ column of output. The keys of the dictionary are the
+ lengths of the items to be output, and the value associated
+ with a key is the number of items of that length.
+ :param total_width: the total width of the output.
+ :param columns: a list containing the minimum amount of space
+ that must be allocated for each row. This can be used to
+ ensure that there is space available in a column if, for
+ example, the actual lengths of the items being output
+ cannot be given in *data*
+ :param remainder_column: number of the column to receive a few
+ extra spaces that may remain after other allocation has
+ taken place
+ :param indent: string that will be prefixed to a line of
+ output to create e.g. an indent
+ :return: a list of the widths of the columns that the fields
+ in data should be placed into for output
+ """
+ if total_width is None:
+ total_width = self.term.columns
+
+ cols = len(data)
+ # Convert the data to ascending list of tuples, (field_length, pkgs)
+ pdata = data
+ data = [None] * cols # Don't modify the passed in data
+ for d in range(0, cols):
+ data[d] = sorted(pdata[d].items())
+
+ # We start allocating 1 char to everything but the last column, and a
+ # space between each (again, except for the last column). Because
+ # at worst we are better with:
+ # |one two three|
+ # | four |
+ # ...than:
+ # |one two three|
+ # | f|
+ # |our |
+ # ...the later being what we get if we pre-allocate the last column, and
+ # thus. the space, due to "three" overflowing it's column by 2 chars.
+ if columns is None:
+ columns = [1] * (cols - 1)
+ columns.append(0)
+
+ total_width -= (sum(columns) + (cols - 1) +
+ utf8_width(indent))
+ if not columns[-1]:
+ total_width += 1
+ while total_width > 0:
+ # Find which field all the spaces left will help best
+ helps = 0
+ val = 0
+ for d in xrange(0, cols):
+ thelps = self._calc_columns_spaces_helps(columns[d], data[d],
+ total_width)
+ if not thelps:
+ continue
+ # We prefer to overflow: the last column, and then earlier
+ # columns. This is so that in the best case (just overflow the
+ # last) ... grep still "works", and then we make it prettier.
+ if helps and (d == (cols - 1)) and (thelps / 2) < helps:
+ continue
+ if thelps < helps:
+ continue
+ helps = thelps
+ val = d
+
+ # If we found a column to expand, move up to the next level with
+ # that column and start again with any remaining space.
+ if helps:
+ diff = data[val].pop(0)[0] - columns[val]
+ if not columns[val] and (val == (cols - 1)):
+ # If we are going from 0 => N on the last column, take 1
+ # for the space before the column.
+ total_width -= 1
+ columns[val] += diff
+ total_width -= diff
+ continue
+
+ overflowed_columns = 0
+ for d in xrange(0, cols):
+ if not data[d]:
+ continue
+ overflowed_columns += 1
+ if overflowed_columns:
+ # Split the remaining spaces among each overflowed column
+ # equally
+ norm = total_width / overflowed_columns
+ for d in xrange(0, cols):
+ if not data[d]:
+ continue
+ columns[d] += norm
+ total_width -= norm
+
+ # Split the remaining spaces among each column equally, except the
+ # last one. And put the rest into the remainder column
+ cols -= 1
+ norm = total_width / cols
+ for d in xrange(0, cols):
+ columns[d] += norm
+ columns[remainder_column] += total_width - (cols * norm)
+ total_width = 0
+
+ return columns
+
+ @staticmethod
+ def _fmt_column_align_width(width):
+ if width < 0:
+ return (u"-", -width)
+ return (u"", width)
+
+ @staticmethod
+ def _calc_columns_spaces_helps(current, data_tups, left):
+ """ Spaces left on the current field will help how many pkgs? """
+ ret = 0
+ for tup in data_tups:
+ if left < (tup[0] - current):
+ break
+ ret += tup[1]
+ return ret
+
+ def _formatTransaction(self, tsInfo):
+ """Return a string containing a human-readable formatted
+ summary of the transaction.
+
+ :param tsInfo: :class:`yum.transactioninfo.TransactionData`
+ instance that contains information about the transaction
+ :return: a string that contains a formatted summary of the
+ transaction
+ """
+ # Sort the packages in the transaction into different lists,
+ # e.g. installed, updated etc
+ tsInfo.makelists(True, True)
+
+ # For each package list, pkglist_lines will contain a tuple
+ # that contains the name of the list, and a list of tuples
+ # with information about each package in the list
+ pkglist_lines = []
+ data = {'n' : {}, 'v' : {}, 'r' : {}}
+ a_wid = 0 # Arch can't get "that big" ... so always use the max.
+
+
+ def _add_line(lines, data, a_wid, po, obsoletes=[]):
+ # Create a tuple of strings that contain the name, arch,
+ # version, repository, size, and obsoletes of the package
+ # given in po. Then, append this tuple to lines. The
+ # strings are formatted so that the tuple can be easily
+ # joined together for output.
+
+
+ (n,a,e,v,r) = po.pkgtup
+
+ # Retrieve the version, repo id, and size of the package
+ # in human-readable form
+ evr = po.printVer()
+ repoid = po.ui_from_repo
+ size = self._format_number(float(po.size))
+
+ if a is None: # gpgkeys are weird
+ a = 'noarch'
+
+ lines.append((n, a, evr, repoid, size, obsoletes))
+ # Create a dict of field_length => number of packages, for
+ # each field.
+ for (d, v) in (("n",len(n)), ("v",len(evr)), ("r",len(repoid))):
+ data[d].setdefault(v, 0)
+ data[d][v] += 1
+ a_wid = max(a_wid, len(a))
+
+ return a_wid
+
+
+
+ # Iterate through the different groups of packages
+ for (action, pkglist) in [(_('Installing'), tsInfo.installed),
+ (_('Updating'), tsInfo.updated),
+ (_('Removing'), tsInfo.removed),
+ (_('Reinstalling'), tsInfo.reinstalled),
+ (_('Downgrading'), tsInfo.downgraded),
+ (_('Installing for dependencies'), tsInfo.depinstalled),
+ (_('Updating for dependencies'), tsInfo.depupdated),
+ (_('Removing for dependencies'), tsInfo.depremoved)]:
+ # Create a list to hold the tuples of strings for each package
+ lines = []
+
+ # Append the tuple for each package to lines, and update a_wid
+ for txmbr in pkglist:
+ a_wid = _add_line(lines, data, a_wid, txmbr.po, txmbr.obsoletes)
+
+ # Append the lines instance for this package list to pkglist_lines
+ pkglist_lines.append((action, lines))
+
+ # # Iterate through other package lists
+ # for (action, pkglist) in [(_('Skipped (dependency problems)'),
+ # self.skipped_packages),
+ # (_('Not installed'), self._not_found_i.values()),
+ # (_('Not available'), self._not_found_a.values())]:
+ # lines = []
+ # for po in pkglist:
+ # a_wid = _add_line(lines, data, a_wid, po)
+
+ # pkglist_lines.append((action, lines))
+
+ if not data['n']:
+ return u''
+ else:
+ # Change data to a list with the correct number of
+ # columns, in the correct order
+ data = [data['n'], {}, data['v'], data['r'], {}]
+
+
+
+ # Calculate the space needed for each column
+ columns = [1, a_wid, 1, 1, 5]
+
+ columns = self._calcColumns(data, self.opts.output_width,
+ columns, remainder_column = 2, indent=" ")
+
+ (n_wid, a_wid, v_wid, r_wid, s_wid) = columns
+ assert s_wid == 5
+
+ # out will contain the output as a list of strings, that
+ # can be later joined together
+ out = [u"""
+%s
+%s
+%s
+""" % ('=' * self.opts.output_width,
+ self._fmtColumns(((_('Package'), -n_wid), (_('Arch'), -a_wid),
+ (_('Version'), -v_wid), (_('Repository'), -r_wid),
+ (_('Size'), s_wid)), u" "),
+ '=' * self.opts.output_width)]
+
+ # Add output for each package list in pkglist_lines
+ for (action, lines) in pkglist_lines:
+ #If the package list is empty, skip it
+ if not lines:
+ continue
+
+ # Add the name of the package list
+ totalmsg = u"%s:\n" % action
+ # Add a line of output about an individual package
+ for (n, a, evr, repoid, size, obsoletes) in lines:
+ columns = ((n, -n_wid), (a, -a_wid),
+ (evr, -v_wid), (repoid, -r_wid), (size, s_wid))
+ msg = self._fmtColumns(columns, u" ", u"\n")
+ for obspo in sorted(obsoletes):
+ appended = _(' replacing %s.%s %s\n')
+ appended %= (obspo.name,
+ obspo.arch, obspo.printVer())
+ msg = msg+appended
+ totalmsg = totalmsg + msg
+
+ # Append the line about the individual package to out
+ out.append(totalmsg)
+
+ # Add a summary of the transaction
+ out.append(_("""
+Transaction Summary
+%s
+""") % ('=' * self.opts.output_width))
+ summary_data = (
+ (_('Install'), len(tsInfo.installed),
+ len(tsInfo.depinstalled)),
+ (_('Upgrade'), len(tsInfo.updated),
+ len(tsInfo.depupdated)),
+ (_('Remove'), len(tsInfo.removed),
+ len(tsInfo.depremoved)),
+ (_('Reinstall'), len(tsInfo.reinstalled), 0),
+ (_('Downgrade'), len(tsInfo.downgraded), 0),
+ # (_('Skipped (dependency problems)'), len(self.skipped_packages), 0),
+ # (_('Not installed'), len(self._not_found_i.values()), 0),
+ # (_('Not available'), len(self._not_found_a.values()), 0),
+ )
+ max_msg_action = 0
+ max_msg_count = 0
+ max_msg_pkgs = 0
+ max_msg_depcount = 0
+ for action, count, depcount in summary_data:
+ if not count and not depcount:
+ continue
+
+ msg_pkgs = P_('Package', 'Packages', count)
+ len_msg_action = utf8_width(action)
+ len_msg_count = utf8_width(str(count))
+ len_msg_pkgs = utf8_width(msg_pkgs)
+
+ if depcount:
+ len_msg_depcount = utf8_width(str(depcount))
+ else:
+ len_msg_depcount = 0
+
+ max_msg_action = max(len_msg_action, max_msg_action)
+ max_msg_count = max(len_msg_count, max_msg_count)
+ max_msg_pkgs = max(len_msg_pkgs, max_msg_pkgs)
+ max_msg_depcount = max(len_msg_depcount, max_msg_depcount)
+
+ for action, count, depcount in summary_data:
+ msg_pkgs = P_('Package', 'Packages', count)
+ if depcount:
+ msg_deppkgs = P_('Dependent package', 'Dependent packages',
+ depcount)
+ if count:
+ msg = '%s %*d %s (+%*d %s)\n'
+ out.append(msg % (utf8_width_fill(action, max_msg_action),
+ max_msg_count, count,
+ utf8_width_fill(msg_pkgs, max_msg_pkgs),
+ max_msg_depcount, depcount, msg_deppkgs))
+ else:
+ msg = '%s %*s %s ( %*d %s)\n'
+ out.append(msg % (utf8_width_fill(action, max_msg_action),
+ max_msg_count, '',
+ utf8_width_fill('', max_msg_pkgs),
+ max_msg_depcount, depcount, msg_deppkgs))
+ elif count:
+ msg = '%s %*d %s\n'
+ out.append(msg % (utf8_width_fill(action, max_msg_action),
+ max_msg_count, count, msg_pkgs))
+
+ return ''.join(out)
+
+
+class EmailEmitter(UpdateEmitter):
+ """Emitter class to send messages via email."""
+
+ def __init__(self, opts):
+ super(EmailEmitter, self).__init__(opts)
+ self.subject = ""
+
+ def updatesAvailable(self, tsInfo):
+ """Appends a message to the output list stating that there are
+ updates available, and set an appropriate subject line.
+
+ :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
+ instance that contains information about the transaction.
+ """
+ super(EmailEmitter, self).updatesAvailable(tsInfo)
+ self.subject = "Yum: Updates Available on %s" % self.opts.system_name
+
+ def updatesDownloaded(self):
+ """Append a message to the output list stating that updates
+ have been downloaded successfully, and set an appropriate
+ subject line.
+ """
+ self.subject = "Yum: Updates downloaded on %s" % self.opts.system_name
+ super(EmailEmitter, self).updatesDownloaded()
+
+ def updatesInstalled(self):
+ """Append a message to the output list stating that updates
+ have been installed successfully, and set an appropriate
+ subject line.
+ """
+ self.subject = "Yum: Updates installed on %s" % self.opts.system_name
+ super(EmailEmitter, self).updatesInstalled()
+
+ def setupFailed(self, errmsg):
+ """Append a message to the output list stating that setup
+ failed, and then call sendMessages to emit the output, and set
+ an appropriate subject line.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.subject = "Yum: Failed to perform setup on %s" % self.opts.system_name
+ super(EmailEmitter, self).setupFailed(errmsg)
+
+ def lockFailed(self, errmsg):
+ """Append a message to the output list stating that the
+ program failed to acquire the yum lock, then call sendMessages
+ to emit the output, and set an appropriate subject line.
+
+ :param errmsg: a string that contains the error message
+ """
+ self.subject = "Yum: Failed to acquire the yum lock on %s" % self.opts.system_name
+ super(EmailEmitter, self).lockFailed(errmsg)
+
+ def checkFailed(self, errmsg):
+ """Append a message to the output stating that checking for
+ updates failed, then call sendMessages to emit the output, and
+ set an appropriate subject line.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.subject = "Yum: Failed to check for updates on %s" % self.opts.system_name
+ super(EmailEmitter, self).checkFailed(errmsg)
+
+ def downloadFailed(self, errmsg):
+ """Append a message to the output list stating that checking
+ for group updates failed, then call sendMessages to emit the
+ output, and add an appropriate subject line.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.subject = "Yum: Failed to download updates on %s" % self.opts.system_name
+ super(EmailEmitter, self).lockFailed(errmsg)
+
+ def updatesFailed(self, errmsg):
+ """Append a message to the output list stating that installing
+ updates failed, then call sendMessages to emit the output, and
+ add an appropriate subject line.
+
+ :param errmsgs: a string that contains the error message
+ """
+ self.subject = "Yum: Failed to install updates on %s" % self.opts.system_name
+ super(EmailEmitter, self).updatesFailed(errmsg)
+
+ def sendMessages(self):
+ """Combine the stored messages that have been stored into a
+ single email message, and send this message.
+ """
+ # Build up the email to be sent
+ msg = MIMEText(''.join(self.output))
+ msg['Subject'] = self.subject
+ msg['From'] = self.opts.email_from
+ msg['To'] = ",".join(self.opts.email_to)
+
+ # Send the email
+ s = smtplib.SMTP()
+ s.connect(self.opts.email_host)
+ s.sendmail(self.opts.email_from, self.opts.email_to, msg.as_string())
+ s.close()
+
+
+class StdIOEmitter(UpdateEmitter):
+ """Emitter class to send messages to syslog."""
+
+ def __init__(self, opts):
+ super(StdIOEmitter, self).__init__(opts)
+
+ def sendMessages(self) :
+ """Combine the stored messages that have been stored into a
+ single email message, and send this message to standard output.
+ """
+ print "".join(self.output)
+
+
+class YumCronConfig(BaseConfig):
+ """Class to parse configuration information from the config file, and
+ to store this information.
+ """
+ nonroot_workdir = Option("/var/tmp/yum-cron")
+ system_name = Option(gethostname())
+ output_width = IntOption(80)
+ random_sleep = IntOption(0)
+ emit_via = ListOption(['email','stdio'])
+ email_to = ListOption(["root"])
+ email_from = Option("root")
+ email_host = Option("localhost")
+ email_port = IntOption(25)
+ update_messages = BoolOption(False)
+ apply_updates = BoolOption(False)
+ download_updates = BoolOption(False)
+ yum_config_file = Option("/etc/yum.conf")
+ group_list = ListOption([])
+ group_package_types = ListOption(['mandatory', 'default'])
+
+
+class YumCronBase(yum.YumBase):
+ """Main class to check for and apply the updates."""
+
+ def __init__(self, config_file_name = None):
+ """Create a YumCronBase object, and perform initial setup.
+
+ :param config_file_name: a String specifying the name of the
+ config file to use.
+ """
+ yum.YumBase.__init__(self)
+
+ # Read the config file
+ self.readConfigFile(config_file_name)
+
+
+ # Create the emitters, and add them to the list
+ self.emitters = []
+ if 'email' in self.opts.emit_via:
+ self.emitters.append(EmailEmitter(self.opts))
+ if 'stdio' in self.opts.emit_via:
+ self.emitters.append(StdIOEmitter(self.opts))
+
+ self.updateInfo = []
+ self.updateInfoTime = None
+
+ def readConfigFile(self, config_file_name = None):
+ """Reads the given config file, or if none is given, the
+ default config file.
+
+ :param config_file_name: a String specifying the name of the
+ config file to read.
+ """
+ # Create ConfigParser and UDConfig Objects
+ confparser = ConfigParser()
+ self.opts = YumCronConfig()
+
+ #If no config file name is given, fall back to the default
+ if config_file_name == None:
+ config_file_name = default_config_file
+
+ # Attempt to read the config file. confparser.read will return a
+ # list of the files that were read successfully, so check that it
+ # contains config_file
+ if config_file_name not in confparser.read(config_file_name):
+ print >> sys.stderr, "Error reading config file"
+ sys.exit(1)
+
+ # Populate the values into the opts object
+ self.opts.populate(confparser, 'commands')
+ self.opts.populate(confparser, 'emitters')
+ self.opts.populate(confparser, 'email')
+ self.opts.populate(confparser, 'groups')
+
+ #If the system name is not given, set it by getting the hostname
+ if self.opts.system_name == 'None' :
+ self.opts.system_name = gethostname()
+
+ if 'None' in self.opts.group_list:
+ self.opts.group_list = []
+
+
+ def randomSleep(self, duration):
+ """Sleep for a random amount of time up to *duration*.
+
+ :param duration: the maximum amount of time to sleep, in
+ minutes. The actual time slept will be between 0 and
+ *duration* minutes
+ """
+ if duration > 0:
+ sleep(random() * 60 * duration)
+
+ def doSetup(self):
+ """Perform set up, including setting up directories and
+ parsing options.
+
+ :return: boolean that indicates whether setup has completed
+ successfully
+ """
+ try :
+ # Set the configuration file
+ self.preconf.fn = self.opts.yum_config_file
+
+ # 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)
+
+ # Create the configuration
+ self.conf
+
+ except Exception, e:
+ # If there are any exceptions, send a message about them,
+ # and return False
+ self.emitSetupFailed('%s' % e)
+ sys.exit(1)
+
+ def acquireLock(self):
+ """ Wrapper method around doLock to emit errors correctly."""
+
+ try:
+ self.doLock()
+ except yum.Errors.LockError, e:
+ self.emitLockFailed("%s" % e)
+ sys.exit(1)
+
+ def populateUpdateMetadata(self):
+ """Populate the metadata for the packages in the update."""
+
+ 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 refreshUpdates(self):
+ """Check whether updates are available.
+
+ :return: Boolean indicating whether any updates are
+ available
+ """
+ try:
+ updatesTuples = self.up.getUpdatesTuples()
+ # If there are no updates, return False
+ if not updatesTuples:
+ return False
+
+ # figure out the updates
+ for (new, old) in updatesTuples:
+ updates_available = True
+ 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)
+
+ except Exception, e:
+ self.emitCheckFailed("%s" %(e,))
+ sys.exit(1)
+
+ else:
+ return True
+
+ def refreshGroupUpdates(self):
+ """Check for group updates, and add them to the
+ transaction.
+
+ :return: Boolean indicating whether there are any updates to
+ the group available
+ """
+ update_available = False
+ try:
+ for group_string in self.opts.group_list:
+ group_matched = False
+ for group in self.comps.return_groups(group_string):
+ group_matched = True
+ try:
+ txmbrs = self.selectGroup(group.groupid,
+ self.opts.group_package_types,
+ upgrade=True)
+
+ # If updates are available from a previous
+ # group, or there are updates are available
+ # from this group, set update_available to True
+ update_available |= (txmbrs != [])
+
+ except yum.Errors.GroupsError:
+ self.emitGroupError('Warning: Group %s does not exist.' % group_string)
+ continue
+
+ if not group_matched:
+ self.emitGroupError('Warning: Group %s does not exist.' % group_string)
+ continue
+
+ except Exception, e:
+ self.emitGroupFailed("%s" % e)
+ return False
+
+ else:
+ return update_available
+
+ def findDeps(self):
+ """Build the transaction to resolve the dependencies for the update."""
+
+ try:
+ (res, resmsg) = self.buildTransaction()
+ except yum.Errors.RepoError, e:
+ self.emitCheckFailed("%s" %(e,))
+ sys.exit()
+ if res != 2:
+ self.emitCheckFailed("Failed to build transaction: %s" %(str.join("\n", resmsg),))
+ sys.exit(1)
+
+ def downloadUpdates(self, emit):
+ """Download the update.
+
+ :param emit: Boolean indicating whether to emit messages
+ about the download
+ """
+ # Emit a message that that updates will be downloaded
+ if emit :
+ self.emitDownloading()
+ dlpkgs = map(lambda x: x.po, filter(lambda txmbr:
+ txmbr.ts_state in ("i", "u"),
+ self.tsInfo.getMembers()))
+ try:
+ # Download the updates
+ self.downloadPkgs(dlpkgs)
+ except Exception, e:
+ self.emitDownloadFailed("%s" % e)
+ sys.exit(1)
+ else :
+ # Emit a message that the packages have been downloaded
+ # successfully
+ if emit :
+ self.emitDownloaded()
+ self.emitMessages()
+
+ def installUpdates(self, emit):
+ """Apply the available updates.
+
+ :param emit: Boolean indicating whether to emit messages about
+ the installation
+ """
+ # Emit a message that
+ if emit :
+ self.emitInstalling()
+
+ dlpkgs = map(lambda x: x.po, filter(lambda txmbr:
+ txmbr.ts_state in ("i", "u"),
+ self.tsInfo.getMembers()))
+
+ for po in dlpkgs:
+ result, err = self.sigCheckPkg(po)
+ if result == 0:
+ continue
+ elif result == 1:
+ try:
+ self.getKeyForPackage(po)
+ except yum.Errors.YumBaseError, errmsg:
+ self.emitUpdateFailed([str(errmsg)])
+ return False
+
+ del self.ts
+ self.initActionTs() # make a new, blank ts to populate
+ self.populateTs(keepold=0)
+ self.ts.check() #required for ordering
+ self.ts.order() # order
+ cb = callback.RPMInstallCallback(output = 0)
+ cb.filelog = True
+
+ cb.tsInfo = self.tsInfo
+ try:
+ self.runTransaction(cb=cb)
+ except yum.Errors.YumBaseError, err:
+
+ self.emitUpdateFailed([str(err)])
+ sys.exit(1)
+
+ self.emitInstalled()
+ self.emitMessages()
+
+ def updatesCheck(self):
+ """Check to see whether updates are available for any
+ installed packages. If updates are available, install them,
+ download them, or just emit a message, depending on what
+ options are selected in the configuration file.
+ """
+ # Sleep a random time
+ self.randomSleep(self.opts.random_sleep)
+
+ # Perform the initial setup
+ self.doSetup()
+
+ # Acquire the yum lock
+ self.acquireLock()
+
+ # Update the metadata
+ self.populateUpdateMetadata()
+
+ # Exit if we don't need to send messages, or there are no
+ # updates
+ if not (self.opts.update_messages and (self.refreshUpdates()
+ or self.refreshGroupUpdates())):
+ sys.exit(0)
+
+ # Build the transaction to find the additional dependencies
+ self.findDeps()
+
+ # download if set up to do so, else tell about the updates and exit
+ if not self.opts.download_updates:
+ self.emitAvailable()
+ self.emitMessages()
+ self.releaseLocks()
+ sys.exit(0)
+
+ self.downloadUpdates(not self.opts.apply_updates)
+
+ # now apply if we're set up to do so; else just tell that things are
+ # available
+ if not self.opts.apply_updates:
+ self.releaseLocks()
+ sys.exit(0)
+
+ self.installUpdates(True)
+
+ self.releaseLocks()
+ sys.exit(0)
+
+ def releaseLocks(self):
+ """Close the rpm database, and release the yum lock."""
+ self.closeRpmDB()
+ self.doUnlock()
+
+ def emitAvailable(self):
+ """Emit a notice stating whether updates are available."""
+ map(lambda x: x.updatesAvailable(self.tsInfo), self.emitters)
+
+ def emitDownloading(self):
+ """Emit a notice stating that updates are downloading."""
+ map(lambda x: x.updatesDownloading(self.tsInfo), self.emitters)
+
+ def emitDownloaded(self):
+ """Emit a notice stating that updates have downloaded."""
+ map(lambda x: x.updatesDownloaded(), self.emitters)
+
+ def emitInstalling(self):
+ """Emit a notice stating that automatic updates are about to
+ be applied.
+ """
+ map(lambda x: x.updatesInstalling(self.tsInfo), self.emitters)
+
+ def emitInstalled(self):
+ """Emit a notice stating that automatic updates have been applied."""
+ map(lambda x: x.updatesInstalled(), self.emitters)
+
+ def emitSetupFailed(self, error):
+ """Emit a notice stating that checking for updates failed."""
+ map(lambda x: x.setupFailed(error), self.emitters)
+
+ def emitLockFailed(self, errmsg):
+ """Emit a notice that we failed to acquire the yum lock."""
+ map(lambda x: x.lockFailed(errmsg), self.emitters)
+
+ def emitCheckFailed(self, error):
+ """Emit a notice stating that checking for updates failed."""
+ map(lambda x: x.checkFailed(error), self.emitters)
+
+ def emitGroupError(self, error):
+ """Emit a notice stating that there was an error checking for
+ group updates.
+ """
+ map(lambda x: x.groupError(error), self.emitters)
+
+ def emitGroupFailed(self, error):
+ """Emit a notice stating that checking for group updates failed."""
+ map(lambda x: x.groupFailed(error), self.emitters)
+
+ def emitDownloadFailed(self, error):
+ """Emit a notice stating that downloading the updates failed."""
+ map(lambda x: x.downloadFailed(error), self.emitters)
+
+ def emitUpdateFailed(self, errmsgs):
+ """Emit a notice stating that automatic updates failed."""
+ map(lambda x: x.updatesFailed(errmsgs), self.emitters)
+
+ def emitMessages(self):
+ """Emit the messages from the emitters."""
+ map(lambda x: x.sendMessages(), self.emitters)
+
+
+def main():
+ """Configure and run the update check."""
+ # If a file name was passed in, use it as the config file name.
+ base = None
+ if len(sys.argv) > 1:
+ base = YumCronBase(sys.argv[1])
+ else:
+ base = YumCronBase()
+
+ #Run the update check
+ base.updatesCheck()
+
+if __name__ == "__main__":
+ main()
diff --git a/yum-cron/yum-cron.sh b/yum-cron/yum-cron.sh
deleted file mode 100755
index e300fa7..0000000
--- a/yum-cron/yum-cron.sh
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/bin/bash
-
-# This script is designed to be run from cron to automatically keep your
-# system up to date with the latest security patches and bug fixes. It
-# can download and/or apply package updates as configured in
-# /etc/sysconfig/yum-cron.
-
-
-# This is used by /etc/init.d/yum-cron on shutdown to protect against
-# abruptly shutting down mid-transaction. Therefore, you shouldn't change
-# it without changing that.
-PIDFILE=/var/lock/yum-cron.pid
-
-# This is the home of the yum scripts which power the various actions the
-# yum-cron system performs.
-SCRIPTDIR=/usr/share/yum-cron/
-
-# If no command line options were given, exit with a usage message.
-if [[ -z "$1" ]]; then
- echo "Usage: yum-cron {update|cleanup|...}"
- exit 1
-else
- ACTION=$1
-fi
-
-# If a command line option was given, it must match a yum script.
-YUMSCRIPT=${SCRIPTDIR}/${ACTION}.yum
-if [[ ! -r $YUMSCRIPT ]]; then
- echo "Script for action \"$ACTION\" is not readable in $SCRIPTDIR."
- exit 1
-fi
-
-# This must be set above. But there a couple of places where horrible
-# things might happen if it's unset, so, let's check.
-if [[ -z "$PIDFILE" ]]; then
- echo "Error! \$PIDFILE variable must be set in yum-cron."
- exit 1
-fi
-
-# Read the settings from our config file.
-if [[ -f /etc/sysconfig/yum-cron ]]; then
- source /etc/sysconfig/yum-cron
-fi
-
-# If no system name is set, use the hostname.
-[[ -z "$SYSTEMNAME" ]] && SYSTEMNAME=$( hostname )
-
-# If DOWNLOAD_ONLY is set, then we force CHECK_ONLY too.
-# Gotta check for updates before we can possibly download them.
-[[ "$DOWNLOAD_ONLY" == "yes" ]] && CHECK_ONLY=yes
-
-# This holds the output from the "meat" of this script, so that it can
-# be nicely mailed to the configured destination when we're done.
-YUMOUTPUT=$(mktemp /var/run/yum-cron.XXXXXX)
-touch $YUMOUTPUT
-[[ -x /sbin/restorecon ]] && /sbin/restorecon $YUMOUTPUT
-
-# Here is the gigantic block of lockfile logic.
-#
-# Note: the lockfile code doesn't currently try and use YUMOUTPUT to email
-# messages nicely, so this gets handled by normal cron error mailing.
-#
-
-# We use noclobber for the pidfile, as this will test for and if possible
-# create the file in one atomic action. (So there's no race condition.) The
-# current process ID is stored in the file so we can check for staleness
-# later.
-if (set -o noclobber; echo "$$" > $PIDFILE) 2>/dev/null; then
- # We got the lock. So now, set a trap to clean up locks and the output
- # tempfile when the script exits or is killed.
- trap "rm -f $PIDFILE" INT TERM EXIT
-else
- # Lock failed -- check if a running process exists.
- # First, if there's no PID file in the lock directory, something bad has
- # happened. We can't know the process name, so, restart.
- if [[ ! -f $PIDFILE ]]; then
- echo "yum-cron: no lock PID, restarting myself" >&2
- exec $0 "$@"
- fi
- OTHERPID="$(< $PIDFILE)" 2>/dev/null
- # if cat wasn't able to read the file anymore, another instance probably is
- # about to remove the lock -- exit, we're *still* locked
- if [[ $? != 0 ]]; then
- echo "yum-cron: lock failed, PID ${OTHERPID} is active" >&2
- exit 0
- fi
- if ! kill -0 $OTHERPID &>/dev/null; then
- # Lock is stale. Remove it and restart.
- echo "yum-cron: removing stale lock of nonexistant PID ${OTHERPID}" >&2
- rm -f $PIDFILE
- echo "yum-cron: restarting myself" >&2
- exec $0 "$@"
- else
- # Remove lockfiles more than a day old -- they must be stale.
- find $PIDFILE -type f -amin +1440 -exec rm -f $PIDFILE \;
- # If it's still there, it *wasn't* too old. Bail!
- if [[ -f $PIDFILE ]]; then
- # Lock is valid and OTHERPID is active -- exit, we're locked!
- echo "yum-cron: lock failed, PID ${OTHERPID} is active" >&2
- exit 0
- else
- # Lock was invalid. Restart.
- echo "yum-cron: removed stale lock belonging to PID ${OTHERPID}; restarting." >&2
- exec $0 "$@"
- fi
- fi
-fi
-
-# Now, do the actual work.
-
-# We special case "update" because it has complicated conditionals; for
-# everything else we just run yum with the right parameters and
-# corresponding script. Right now, that's just "cleanup" but theoretically
-# there could be other actions.
-{
- case "$ACTION" in
- update)
- # There's three broad possibilties here:
- # CHECK_ONLY (possibly with DOWNLOAD_ONLY)
- # CHECK_FIRST (exits _silently_ if we can't access the repos)
- # nothing special -- just do it
- # Note that in all cases, yum is updated first, and then
- # everything else.
- if [[ "$CHECK_ONLY" == "yes" ]]; then
- /usr/bin/yum $YUM_PARAMETER -e 0 -d 0 -y check-update 1> /dev/null 2>&1
- case $? in
- 1) exit 1;;
- 100) echo "New updates available for host $SYSTEMNAME";
- /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y -C check-update
- if [[ "$DOWNLOAD_ONLY" == "yes" ]]; then
- /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y --downloadonly update
- echo "Updates downloaded. Use \"yum -C update\" manually to install them."
- fi
- ;;
- esac
- elif [[ "$CHECK_FIRST" == "yes" ]]; then
- # Don't run if we can't access the repos -- if this is set,
- # and there's a problem, we exit silently (but return an error
- # code).
- /usr/bin/yum $YUM_PARAMETER -e 0 -d 0 check-update 2>&-
- case $? in
- 1) exit 1;;
- 100) /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y update yum
- /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $YUMSCRIPT
- ;;
- esac
- else
- # and here's the "just do it".
- /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y update yum
- /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $YUMSCRIPT
- fi
- ;;
- *)
- /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $YUMSCRIPT
- ;;
- esac
-
-} >> $YUMOUTPUT 2>&1
-
-if [[ ! -z "$MAILTO" && -x /bin/mail ]]; then
-# If MAILTO is set, use mail command for prettier output.
- [[ -s "$YUMOUTPUT" ]] && \
- mail -s "System update: $SYSTEMNAME" $MAILTO < $YUMOUTPUT && \
- rm -f $YUMOUTPUT
-else
-# The default behavior is to use cron's internal mailing of output.
- cat $YUMOUTPUT && rm -f $YUMOUTPUT
-fi
-
-exit 0
diff --git a/yum.spec b/yum.spec
index 3f73b4a..728071f 100644
--- a/yum.spec
+++ b/yum.spec
@@ -282,8 +282,10 @@ exit 0
%defattr(-,root,root)
%doc COPYING
%config(noreplace) %{_sysconfdir}/cron.daily/0yum-update.cron
+%config(noreplace) %{_sysconfdir}/yum/yum-cron.conf
%{_sysconfdir}/rc.d/init.d/yum-cron
%{_sbindir}/yum-cron
+%{_mandir}/man*/yum-cron.*
%if %{yum_updatesd}
%files updatesd
commit 4fc370a9f9f3837caaaddf41745dc016badab7b2
Author: ZdenÄk Pavlas <zpavlas at redhat.com>
Date: Thu Oct 25 13:33:51 2012 +0200
yum-cron: remove files we don't need
update.yum, cleanup.yum: we no longer run "yum shell"
yum-cron.sysconfig: config stuff goes to /etc/yum-cron.conf
yum-cleanup.cron.sh: no separate cleanup job
diff --git a/yum-cron/Makefile b/yum-cron/Makefile
index 34cb397..4c26969 100644
--- a/yum-cron/Makefile
+++ b/yum-cron/Makefile
@@ -8,14 +8,8 @@ install:
mkdir -p $(DESTDIR)/etc/cron.daily
mkdir -p $(DESTDIR)/etc/rc.d/init.d
mkdir -p $(DESTDIR)/usr/sbin
- mkdir -p $(DESTDIR)/etc/sysconfig
- mkdir -p $(DESTDIR)/usr/share/yum-cron
# Install yum-update.cron as 0yum-update.cron so it runs before items like
# manpage update, mlocate, and prelink
install -D -m 755 yum-update.cron.sh $(DESTDIR)/etc/cron.daily/0yum-update.cron
- install -D -m 755 yum-cleanup.cron.sh $(DESTDIR)/etc/cron.daily/yum-cleanup.cron
install -D -m 755 yum-cron.sysvinit $(DESTDIR)/etc/rc.d/init.d/yum-cron
install -D -m 755 yum-cron.sh $(DESTDIR)/usr/sbin/yum-cron
- install -D -m 644 yum-cron.sysconfig $(DESTDIR)/etc/sysconfig/yum-cron
- install -D -m 644 update.yum $(DESTDIR)/usr/share/yum-cron/update.yum
- install -D -m 644 cleanup.yum $(DESTDIR)/usr/share/yum-cron/cleanup.yum
diff --git a/yum-cron/cleanup.yum b/yum-cron/cleanup.yum
deleted file mode 100644
index c60fa08..0000000
--- a/yum-cron/cleanup.yum
+++ /dev/null
@@ -1,4 +0,0 @@
-clean packages
-clean expire-cache
-ts run
-exit
diff --git a/yum-cron/update.yum b/yum-cron/update.yum
deleted file mode 100644
index 5d4e874..0000000
--- a/yum-cron/update.yum
+++ /dev/null
@@ -1,3 +0,0 @@
-update
-ts run
-exit
diff --git a/yum-cron/yum-cleanup.cron.sh b/yum-cron/yum-cleanup.cron.sh
deleted file mode 100755
index e38e80f..0000000
--- a/yum-cron/yum-cleanup.cron.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-# Only run if this flag is set. The flag is created by the yum-cron init
-# script when the service is started -- this allows one to use chkconfig and
-# the standard "service stop|start" commands to enable or disable yum-cron.
-if [[ ! -f /var/lock/subsys/yum-cron ]]; then
- exit 0
-fi
-
-# Read configuration settings from the sysconfig directory.
-if [[ -f /etc/sysconfig/yum-cron ]]; then
- source /etc/sysconfig/yum-cron
-fi
-
-# Only run on certain days of the week, based on the
-# settings in the above-mentioned sysconfig file.
-dow=`date +%w`
-DAYS_OF_WEEK=${DAYS_OF_WEEK:-0123456}
-if [[ "${DAYS_OF_WEEK/$dow/}" == "${DAYS_OF_WEEK}" ]]; then
- exit 0
-fi
-
-# And only _clean_ on a subset of the configured days.
-CLEANDAY=${CLEANDAY:-0}
-if [[ "${CLEANDAY/$dow/}" == "${CLEANDAY}" ]]; then
- exit 0
-fi
-
-# Action!
-exec /usr/sbin/yum-cron cleanup
diff --git a/yum-cron/yum-cron.sysconfig b/yum-cron/yum-cron.sysconfig
deleted file mode 100644
index 53477d3..0000000
--- a/yum-cron/yum-cron.sysconfig
+++ /dev/null
@@ -1,92 +0,0 @@
-# This is the configuration file for yum-cron, a simple system for
-# keeping your machine up to date. These options are used variously by
-# the main script, by the cron scripts, and by the init script.
-
-# Main Options
-#--------------------------------------------------------------------------
-
-# Pass any given parameter to yum, as run in all the scripts invoked by
-# this package. Be aware that this is global, and yum is invoked in
-# several modes by these scripts, and your parameter might not be
-# appropriate in all cases.
-YUM_PARAMETER=
-
-# Don't install; just check and report.
-# (Valid options: yes|no)
-CHECK_ONLY=no
-
-# Don't install; just check for and download any pending updates. This
-# implies CHECK_ONLY=yes, as we've gotta check first to see what to
-# download.
-# (Valid options: yes|no)
-DOWNLOAD_ONLY=no
-
-# Check to see if we can reach the repos before attempting an update.
-# If there is an error, exit silently with no output. You might want
-# this if you know your network connectivity is sporadic.
-# (Valid options: yes|no)
-CHECK_FIRST=no
-
-
-# Yum error level. The practical range is 0-10, where 0 means print
-# only critical errors, and 10 means print all errors, even ones that
-# are not important. Level 0 is the default if nothing is set.
-ERROR_LEVEL=0
-
-# Yum debug level. The practical range is 0-10; a higher number means
-# more output. Level 1 is a useful level if you want to see what's been
-# done and don't want to read /var/log/yum.log. Level 0 is the default
-# if no value is set here.
-DEBUG_LEVEL=0
-
-# If MAILTO is set and the /bin/mail command is available, the mail
-# command is used to deliver yum output. If MAILTO is unset, crond will
-# send the output by itself, usually to root (but with a less useful
-# subject line).
-MAILTO=root
-
-# The reports generated by this command generally use the hostname of
-# the system as reported by the hostname command. If you'd prefer to
-# use something else, you can set that here.
-#SYSTEMNAME=""
-
-# Scheduling Options (used by the default cron scripts,
-# /etc/cron.daily/yum-cleanup.cron and /etc/cron.daily/0yum-update.cron)
-#
-# Note that if you use a different cron configuration (for example,
-# removing the default scripts and adding an entry in /etc/cron.d),
-# these values will have no effect -- unless you read and act on them
-# in your new configuration.
-#--------------------------------------------------------------------------
-
-# Wait for a random time up to the given number of minutes before
-# applying updates. With a value of 60, yum-cron will delay between 1
-# and 60 minutes. A value of 0 will result in no delay, which is handy
-# if you want to ensure that updates happen at a known time, but could
-# be bad for update servers to be hit by all clients at exactly the
-# same time.
-RANDOMWAIT=60
-
-# You may set DAYS_OF_WEEK to the numeric days of the week you want to
-# run, where 0 is Sunday and 6 is Saturday. The default is to run every
-# day.
-#DAYS_OF_WEEK="0123456"
-
-# The cleanup task (which clears the package cache) can run on a subset
-# of the days above. (If the value chosen here doesn't appear in
-# DAYS_OF_WEEK, the cleanup task will never happen.)
-CLEANDAY="0"
-
-# Init System Options (used by /etc/init.d/yum-cron)
-#--------------------------------------------------------------------------
-
-# If SERVICE_WAITS is set to "yes", and a transaction is in progress
-# when the yum-cron service is stopped, the init script will wait
-# up to SERVICE_WAIT_TIME seconds before killing the task. Without
-# this, system shutdown continues as normal, potentially breaking
-# in-progress transactions.
-# (Valid options: yes|no)
-SERVICE_WAITS=yes
-
-# 300 is the default.
-SERVICE_WAIT_TIME=300
diff --git a/yum-cron/yum-cron.sysvinit b/yum-cron/yum-cron.sysvinit
index ee531c6..7b44d5e 100755
--- a/yum-cron/yum-cron.sysvinit
+++ b/yum-cron/yum-cron.sysvinit
@@ -7,21 +7,18 @@
# description: This controls whether yum-cron runs. If this service is \
# off, the yum-cron scripts in /etc/cron.daily exit \
# immediately; otherwise, they download and/or apply package \
-# updates as configured in /etc/sysconfig/yum-cron.
+# updates as configured in /etc/yum/yum-cron.conf.
# processname: yum-cron
-# config: /etc/yum/yum-daily.yum
#
# source function library
. /etc/rc.d/init.d/functions
-
-test -f /etc/sysconfig/yum-cron && . /etc/sysconfig/yum-cron
-
lockfile=/var/lock/subsys/yum-cron
# This is generated by /usr/sbin/yum-cron and will exist when that script
# is running and not otherwise.
pidfile=/var/lock/yum-cron.pid
+SERVICE_WAITS=yes
RETVAL=0
diff --git a/yum-cron/yum-update.cron.sh b/yum-cron/yum-update.cron.sh
index b9edddf..06dee5f 100755
--- a/yum-cron/yum-update.cron.sh
+++ b/yum-cron/yum-update.cron.sh
@@ -7,29 +7,5 @@ if [[ ! -f /var/lock/subsys/yum-cron ]]; then
exit 0
fi
-# Read configuration settings from the sysconfig directory.
-if [[ -f /etc/sysconfig/yum-cron ]]; then
- source /etc/sysconfig/yum-cron
-fi
-
-# Only run on certain days of the week, based on the
-# settings in the above-mentioned sysconfig file.
-dow=`date +%w`
-DAYS_OF_WEEK=${DAYS_OF_WEEK:-0123456}
-if [[ "${DAYS_OF_WEEK/$dow/}" == "${DAYS_OF_WEEK}" ]]; then
- exit 0
-fi
-
-# Wait a random number of minutes, again based on
-# the setting in the sysconfig file.
-[[ $RANDOMWAIT -gt 0 ]] && sleep $(( $RANDOM % ($RANDOMWAIT * 60) + 1 ))
-
-# Double-check to make sure that we're still supposed to be
-# active after the random wait.
-if [[ ! -f /var/lock/subsys/yum-cron ]]; then
- exit 0
-fi
-
-
# Action!
-exec /usr/sbin/yum-cron update
+exec /usr/sbin/yum-cron
diff --git a/yum.spec b/yum.spec
index 4ad6804..3f73b4a 100644
--- a/yum.spec
+++ b/yum.spec
@@ -282,13 +282,8 @@ exit 0
%defattr(-,root,root)
%doc COPYING
%config(noreplace) %{_sysconfdir}/cron.daily/0yum-update.cron
-%config(noreplace) %{_sysconfdir}/cron.daily/yum-cleanup.cron
%{_sysconfdir}/rc.d/init.d/yum-cron
%{_sbindir}/yum-cron
-%config(noreplace) %{_sysconfdir}/sysconfig/yum-cron
-%dir %{_datadir}/yum-cron
-%{_datadir}/yum-cron/update.yum
-%{_datadir}/yum-cron/cleanup.yum
%if %{yum_updatesd}
%files updatesd
More information about the Yum-commits
mailing list