530afeafab
Secure boot signing needs intimate knowledge of pxeboot path. This flag will indicate that a /var prefix is required on that path. Story: 2009101 Task: 44197 Change-Id: I8591a5c8383fb367bbf2a2664641f4bc664c1c7b Signed-off-by: Scott Little <scott.little@windriver.com>
539 lines
18 KiB
Bash
Executable File
539 lines
18 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
#
|
|
# Copyright (c) 2018 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
# This script calls into an external signing server to perform signing of some
|
|
# packages in the system. The old packages (which are typically generated by
|
|
# the build system and signed by placeholder keys) are overwritten by the new
|
|
# packages.
|
|
#
|
|
# Three types of packages are signed:
|
|
# kernels (both std and lowlatency, aka "rt", kernels)
|
|
# grub
|
|
# shim
|
|
#
|
|
# Kernels and grub are generated by producing (under the normal build system)
|
|
# two packages -- a package containing the unsigned binaries, and a package
|
|
# containing binaries signed with temporary keys. All the "accessories" (files,
|
|
# scripts, etc) are included in the package containing the signed-with-temp-keys
|
|
# files. The signing server will take both packages, sign the unsigned
|
|
# binaries, and replace the files in the signed package with the newly signed
|
|
# ones.
|
|
#
|
|
# Typical flow/artifacts
|
|
# kernel.src.rpm -> produces kernel.rpm and kernel-unsigned.rpm
|
|
# kernel.rpm -> initially contains binaries signed with a temporary key
|
|
# -> contains all files used by the kernel
|
|
# -> can be installed and used in a system (it just won't
|
|
# secure boot since the key is just a temp key)
|
|
# kernel-unsigned.rpm -> contains just unsigned kernel binaries
|
|
#
|
|
# The signing server will take both packages, sign the binaries in
|
|
# kernel-unsigned.rpm with our private key, and replace the binaries in
|
|
# kernel.rpm with the new binaries. The kernel.rpm can then be replaced by the
|
|
# version generated by the signing server.
|
|
#
|
|
# Shim is a bit of a different beast.
|
|
#
|
|
# There are two source packages - shim and shim-signed. Frustratingly, "shim"
|
|
# source produces a "shim-unsigned" binary output. "shim-signed" produces a
|
|
# "shim" binary output.
|
|
#
|
|
# The "shim-signed" source RPM doesn't contain source code -- it just contains
|
|
# instructions to take the "shim-unsigned" binaries, sign them, and package the
|
|
# output. We've modified the shim-signed RPM to (rather than sign with a temp
|
|
# key) use "presigned" binaries from shim-unsigned if the files exist. (It will
|
|
# still use a temp key of no presigned files are found, which is how the build
|
|
# system normally runs).
|
|
#
|
|
# The signing server will unpack the shim-unsigned package, sign the binaries
|
|
# (as "presigned") and repack the package.
|
|
#
|
|
# A rebuild of shim-signed by the build server is then required.
|
|
#
|
|
# Thanks for bearing with me in the convoluted discussion, above.
|
|
|
|
|
|
# Script flow:
|
|
# - call signing server to sign kernels (if they exist and are new, as with
|
|
# other RPMs)
|
|
# - replace old kernel packages with newly signed ones
|
|
# - call signing server to sign grub (and replace old version with the newly
|
|
# signed one)
|
|
# - call signing server to sign shim-unsigned (replace old version)
|
|
# - rebuild shim-signed
|
|
# - update our repos to advertize all newly replaced packages
|
|
|
|
# check_if_pkg_needs_signing <path/to/filename.rpm>
|
|
#
|
|
# Checks to see if a given package needs to be signed. We maintain a list of
|
|
# MD5 sums for RPMs we have signed. Thus, we can easily see if we've already
|
|
# signed a package.
|
|
#
|
|
# Returns 1 if the package does need signing, or 0 if package does not
|
|
#
|
|
# This function expects the package specified to exist.
|
|
function check_if_pkg_needs_signing
|
|
{
|
|
local PKG_TO_CHECK=$1
|
|
|
|
if [ ! -e ${SIGNED_PKG_DB} ]; then
|
|
# We haven't signed anything before, so this package needs signing
|
|
return 1
|
|
fi
|
|
|
|
local SIGNED_PKG_MD5=`grep ${PKG_TO_CHECK} ${SIGNED_PKG_DB} | cut -d ' ' -f 1`
|
|
if [ "x${SIGNED_PKG_MD5}" == "x" ]; then
|
|
# We have no record of having signed the package -- needs signing
|
|
return 1
|
|
fi
|
|
|
|
local CURRENT_MD5=`md5sum ${PKG_TO_CHECK} | cut -d ' ' -f 1`
|
|
if [ "${CURRENT_MD5}" != "${SIGNED_PKG_MD5}" ]; then
|
|
# The package has been regenerated since we last signed it -- needs
|
|
# signing again
|
|
return 1
|
|
fi
|
|
|
|
# The package md5 sum matches the md5sum of the package when it was last
|
|
# signed.
|
|
return 0
|
|
}
|
|
|
|
# update_signed_pkg_list <path/to/filename.rpm>
|
|
#
|
|
# Updated our list of signed packages with the md5 sum of a recently signed
|
|
# package.
|
|
#
|
|
# This function expects the package to exist.
|
|
function update_signed_pkg_list
|
|
{
|
|
local PKG_TO_ADD=$1
|
|
|
|
if [ ! -e ${SIGNED_PKG_DB} ]; then
|
|
touch ${SIGNED_PKG_DB}
|
|
fi
|
|
|
|
# remove current entry for package
|
|
local TMPFILE=`mktemp`
|
|
grep -v $(basename ${PKG_TO_ADD}) ${SIGNED_PKG_DB} > ${TMPFILE}
|
|
mv ${TMPFILE} ${SIGNED_PKG_DB}
|
|
|
|
# add MD5 for package to the package list
|
|
md5sum ${PKG_TO_ADD} >> ${SIGNED_PKG_DB}
|
|
}
|
|
|
|
|
|
# update_repo <std|rt>
|
|
#
|
|
# Updates either the standard or rt repo with latest packages
|
|
# Checks that you specified a repo, and that the path exists.
|
|
#
|
|
# There are actually now two places we need to update -- the
|
|
# rpmbuild/RPMS/ path, as well as the results/.../ path
|
|
function update_repo
|
|
{
|
|
local BUILD_TYPE=$1
|
|
local EXTRA_PARAMS=""
|
|
local RETCODE=0
|
|
local repopath=""
|
|
|
|
if [ "x$BUILD_TYPE" == "x" ]; then
|
|
return 1
|
|
fi
|
|
|
|
if [ "x$MY_BUILD_ENVIRONMENT_TOP" == "x" ]; then
|
|
return 1
|
|
fi
|
|
|
|
for repopath in "$MY_WORKSPACE/$BUILD_TYPE/rpmbuild/RPMS" "$MY_WORKSPACE/$BUILD_TYPE/results/${MY_BUILD_ENVIRONMENT_TOP}-$BUILD_TYPE"; do
|
|
if [ ! -d "$repopath" ]; then
|
|
echo "Error - cannot find path $repopath"
|
|
return 1
|
|
fi
|
|
|
|
cd $repopath
|
|
if [ -f comps.xml ]; then
|
|
EXTRA_PARAMS="-g comps.xml"
|
|
fi
|
|
createrepo --update $EXTRA_PARAMS . > /dev/null
|
|
RETCODE=$?
|
|
cd - > /dev/null
|
|
if [ 0$RETCODE -ne 0 ]; then
|
|
return $RETCODE
|
|
fi
|
|
done
|
|
|
|
return $RETCODE
|
|
}
|
|
|
|
# sign_shims - find and sign any shim package that we need
|
|
# Note that shim might produce a "shim-unsigned-[verison-release]
|
|
# package (old shim) or shim-unsigned-x64-[v-r] &
|
|
# shim-unsigned-ia32 package (new shim). In the case of new shim,
|
|
# we must do x64 only, and not ia32.
|
|
#
|
|
function sign_shims
|
|
{
|
|
SHIM=`find $MY_WORKSPACE/std/rpmbuild/RPMS -name "shim-unsigned-x64-*.$ARCH.rpm" | grep -v debuginfo`
|
|
if [ -z "$SHIM" ]; then
|
|
SHIM=`find $MY_WORKSPACE/std/rpmbuild/RPMS -name "shim-unsigned-*.$ARCH.rpm" | grep -v debuginfo`
|
|
fi
|
|
if [ -z "${SHIM}" ]; then
|
|
echo "Warning -- cannot find shim package to sign"
|
|
return 0
|
|
fi
|
|
sign shim $SHIM
|
|
|
|
return $?
|
|
}
|
|
|
|
# sign_grubs - find and sign any grub package that we need to.
|
|
# Grub (and kernel) are initially signed with temporary keys, so
|
|
# we need to upload both the complete package, as well as the
|
|
# unsigned binaries
|
|
#
|
|
function sign_grubs
|
|
{
|
|
GRUB=`find $MY_WORKSPACE/std/rpmbuild/RPMS -name "grub2-efi-x64-[1-9]*.$ARCH.rpm"`
|
|
UNSIGNED_GRUB=`find $MY_WORKSPACE/std/rpmbuild/RPMS -name "grub2-efi-x64-unsigned*.$ARCH.rpm"`
|
|
if [ "x${GRUB}" == "x" ]; then
|
|
echo "Warning -- cannot find GRUB package to sign"
|
|
return 0
|
|
fi
|
|
if [ "x${UNSIGNED_GRUB}" == "x" ]; then
|
|
echo "Warning -- cannot find unsigned GRUB package to sign"
|
|
return 0
|
|
fi
|
|
|
|
sign grub2 $GRUB $UNSIGNED_GRUB
|
|
return $?
|
|
}
|
|
|
|
# sign_kernels - find and sign any kernel package that we need to.
|
|
#
|
|
function sign_kernels
|
|
{
|
|
sign_kernel "std" ""
|
|
sign_kernel "rt" "-rt"
|
|
}
|
|
|
|
# sign_kernel - find and sign kernel package if we need to.
|
|
# Kernels (and grub) are initially signed with temporary keys, so
|
|
# we need to upload both the complete package, as well as the
|
|
# unsigned binaries
|
|
function sign_kernel
|
|
{
|
|
local KERNEL_PATH=$1
|
|
local KERNEL_EXTRA=$2
|
|
|
|
local KERNEL=""
|
|
local UNSIGNED_KERNEL=""
|
|
local RPM=""
|
|
local VMLINUZ=""
|
|
|
|
# 5.xx series kernels store vmlinuz in the 'kernel-core' package
|
|
KERNEL=$(find $MY_WORKSPACE/${KERNEL_PATH}/rpmbuild/RPMS -name "kernel${KERNEL_EXTRA}-core-[1-9]*.$ARCH.rpm")
|
|
if [ "x${KERNEL}" == "x" ]; then
|
|
# Older kernels store vmlinuz in the 'kernel' package
|
|
KERNEL=$(find $MY_WORKSPACE/${KERNEL_PATH}/rpmbuild/RPMS -name "kernel${KERNEL_EXTRA}-[1-9]*.$ARCH.rpm")
|
|
if [ "x${KERNEL}" == "x" ]; then
|
|
echo "Warning -- cannot find kernel package to sign in ${KERNEL_PATH}"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# The unsigned vmlinuz is in the 'kernel-unsigned' package for ALL kernels.
|
|
UNSIGNED_KERNEL=$(find $MY_WORKSPACE/${KERNEL_PATH}/rpmbuild/RPMS -name "kernel${KERNEL_EXTRA}-unsigned-[1-9]*.$ARCH.rpm")
|
|
if [ "x${UNSIGNED_KERNEL}" == "x" ]; then
|
|
echo "Warning -- cannot find unsigned kernel package to sign in ${KERNEL_PATH}"
|
|
return 0
|
|
fi
|
|
|
|
# check for vmlinuz
|
|
for RPM in $KERNEL $UNSIGNED_KERNEL; do
|
|
VMLINUZ=$(rpm -q -l -p $RPM | grep '/boot/vmlinuz')
|
|
if [ $? -ne 0 ]; then
|
|
echo "Error -- cannot find /boot/vmlinuz in ${RPM}"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
sign kernel $KERNEL $UNSIGNED_KERNEL
|
|
return $?
|
|
}
|
|
|
|
# rebuild_pkgs - rebuild any packages that need to be updated from the newly
|
|
# signed binaries
|
|
#
|
|
function rebuild_pkgs
|
|
{
|
|
local LOGFILE="$MY_WORKSPACE/export/signed-rebuild.log"
|
|
local PKGS_TO_REBUILD=${REBUILD_LIST}
|
|
|
|
if [ "x${PKGS_TO_REBUILD}" == "x" ]; then
|
|
# No rebuilds required, return cleanly
|
|
return 0
|
|
fi
|
|
|
|
# If we reach this point, then we have one or more packages to be rebuilt
|
|
|
|
# first, update the repo so it is aware of the "latest" binaries
|
|
update_repo std
|
|
if [ $? -ne 0 ]; then
|
|
echo "Could not update signed packages -- could not update repo"
|
|
return 1
|
|
fi
|
|
|
|
echo "Performing rebuild of packages: $PKGS_TO_REBUILD"
|
|
FORMAL_BUILD=0 build-pkgs --no-descendants --no-build-info --no-required --careful --append-log $PKGS_TO_REBUILD > $LOGFILE 2>&1
|
|
|
|
if [ $? -ne 0 ]; then
|
|
echo "Could not rebuild packages: $PKGS_TO_REBUILD -- see $LOGFILE for details"
|
|
return 1
|
|
fi
|
|
|
|
echo "Done"
|
|
return 0
|
|
}
|
|
|
|
# sign <type_of_pkg> <pkg> [pkg_containing_unsigned_bins]
|
|
#
|
|
# This routine uploads a package to the signing server, instructs the signing
|
|
# signing server to do its' magic, and downloads the updated (signed) package
|
|
# from the signing server.
|
|
#
|
|
# Accessing the signing server -- the signing server cannot just be logged
|
|
# into by anyone. A small number of users (Jason McKenna, Scott Little, Greg
|
|
# Waines, etc) have permission to log in as themselves. In addition, there is
|
|
# a user "signing" who is unique to the server. The "jenkins" user on our
|
|
# build servers has permission to login/upload files as "signing" due to Jenkins'
|
|
# private SSH key being added to the signing user's list of keys. This means
|
|
# that Jenkins can upload and run commands on the server as "signing".
|
|
#
|
|
# In addition to uploading files as signing, the signing user has permissions to
|
|
# run a single command (/opt/signing/sign.sh) as a sudo root user. The signing
|
|
# user does not have access to modify the script or to run any other commands as
|
|
# root. The sign.sh script will take inputs (the files that jenkins has
|
|
# uploaded), verify the contents, sign the images against private keys, and
|
|
# output a new .rpm contianing the signed version of the files. Assuming all
|
|
# is successful, the filename of the signed output file is returned, and the
|
|
# jenkins user can then use that filename to download the file (the "signing"
|
|
# user does not have access to remove or modify the file once it's created).
|
|
#
|
|
# All operations done on the signing server are logged in muliple places, and
|
|
# the output RPM artifacts are timestamped to ensure that they are not
|
|
# overwritten by subsequent calls to sign.sh.
|
|
#
|
|
# kernel and grub package types require you to specify/upload the unsigned
|
|
# packages as well as the normal binary
|
|
function sign
|
|
{
|
|
local TYPE=$1
|
|
local FILE=$2
|
|
local UNSIGNED=$3
|
|
local UNSIGNED_OPTION=""
|
|
local TMPFILE=`mktemp /tmp/sign.XXXXXXXX`
|
|
|
|
# Don't sign if we've already signed it
|
|
check_if_pkg_needs_signing ${FILE}
|
|
if [ $? -eq 0 ]; then
|
|
echo "Not signing ${FILE} as we previously signed it"
|
|
return 0
|
|
fi
|
|
|
|
echo "Signing $FILE"
|
|
|
|
# upload the original package
|
|
scp -q $FILE $SIGNING_USER@$SIGNING_SERVER:$UPLOAD_PATH
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to upload file $FILE"
|
|
\rm -f $TMPFILE
|
|
return 1
|
|
fi
|
|
|
|
# upload the unsigned package (if specified)
|
|
if [ "x$UNSIGNED" != "x" ]; then
|
|
echo "Uploading unsigned: $UNSIGNED"
|
|
scp -q $UNSIGNED $SIGNING_USER@$SIGNING_SERVER:$UPLOAD_PATH
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to upload file $UNSIGNED"
|
|
\rm -f $TMPFILE
|
|
return 1
|
|
fi
|
|
UNSIGNED=$(basename $UNSIGNED)
|
|
UNSIGNED_OPTION="-u $UPLOAD_PATH/$UNSIGNED"
|
|
fi
|
|
|
|
# Call the magic script on the signing server. Note that the user
|
|
# ($SIGNING_USER) has sudo permissions but only to invoke this one script.
|
|
# The signing user cannot make other sudo calls.
|
|
#
|
|
# We place output in $TMPFILE to extract the output file name later
|
|
#
|
|
ssh $SIGNING_USER@$SIGNING_SERVER sudo $SIGNING_SCRIPT -v -i $UPLOAD_PATH/$(basename $FILE) $UNSIGNED_OPTION -t $TYPE > $TMPFILE 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
echo "Signing of $FILE failed"
|
|
\rm -f $TMPFILE
|
|
return 1
|
|
fi
|
|
|
|
# The signing server script will output the name by which the newly signed
|
|
# RPM can be found. This will be a unique filename (based on the unique
|
|
# upload directory generated by the "-r" option above).
|
|
#
|
|
# The reason for this is so that we can archive all output files
|
|
# and examine them later without them being overwriten. File paths are
|
|
# typically of the form
|
|
#
|
|
# /export/signed_images/XXXXXXX_grub2-efi-64-2.02-0.44.el7.centos.tis.3.x86_64.rpm
|
|
#
|
|
# Extract the output name, and copy the RPM back into our system
|
|
# (Note that we overwrite our original version of the RPM)
|
|
#
|
|
# Note that some packages (like grub) may produce multiple output RPMs (i.e.
|
|
# multiple lines list output files.
|
|
OUTPUT=`grep "Output written:" $TMPFILE | sed "s/Output written: //"`
|
|
|
|
# Check that we got something
|
|
if [ "x$OUTPUT" == "x" ]; then
|
|
echo "Could not determine output file -- check logs on signing server for errors"
|
|
\cp $TMPFILE $MY_WORKSPACE/export/signing.log
|
|
\rm -f $TMPFILE
|
|
return 1
|
|
fi
|
|
|
|
# The signing script can return multiple output files, if appropriate for
|
|
# the input RPM source type. Copy each output RPM to our repo
|
|
# Note that after we download the file we extract the base package name
|
|
# from the RPM to find the name of the file that it *should* be named
|
|
#
|
|
# example:
|
|
# we'd download "Zrqyeuzw_kernel-3.10.0-514.2.2.el7.20.tis.x86_64.rpm"
|
|
# we'd figure out that the RPM name should be "kernel"
|
|
# we look for "kernel" in the RPM filename, and rename
|
|
# "Zrqyeuzw_kernel-3.10.0-514.2.2.el7.20.tis.x86_64.rpm" to
|
|
# "kernel-3.10.0-514.2.2.el7.20.tis.x86_64.rpm"
|
|
while read OUTPUT_FILE; do
|
|
|
|
# Download the file from the signing server
|
|
local DOWNLOAD_FILENAME=$(basename $OUTPUT_FILE)
|
|
scp -q $SIGNING_USER@$SIGNING_SERVER:$OUTPUT_FILE $(dirname $FILE)
|
|
if [ $? -ne 0 ]; then
|
|
\rm -f $TMPFILE
|
|
echo "Copying file from signing server failed"
|
|
return 1
|
|
fi
|
|
echo "Successfully retrieved $OUTPUT_FILE"
|
|
|
|
# figure out what the file should be named (strip away leading chars)
|
|
local RPM_NAME=`rpm -qp $(dirname $FILE)/$DOWNLOAD_FILENAME --qf="%{name}"`
|
|
local CORRECT_OUTPUT_FILE_NAME=`echo $DOWNLOAD_FILENAME | sed "s/^.*$RPM_NAME/$RPM_NAME/"`
|
|
|
|
# rename the file
|
|
\mv -f $(dirname $FILE)/$DOWNLOAD_FILENAME $(dirname $FILE)/$CORRECT_OUTPUT_FILE_NAME
|
|
|
|
# replace the version of the file in results
|
|
#
|
|
# Potential hiccup in future -- this code currenty replaces any output file in EITHER
|
|
# std or rt results which matches the filename we just downloaded from the signing.
|
|
# server. This means there could be an issue where we sign something-ver-rel.arch.rpm
|
|
# but we expect different versions of that RPM in std and in rt. Currently, we do not
|
|
# have any RPMs which have that problem (all produced RPMs in rt have the "-rt" suffix
|
|
# let along any "signed" rpms) but it's something of which to be aware.
|
|
#
|
|
# Also, note that we do not expect multiple RPMs in each repo to have the same filename.
|
|
# We use "head -n 1" to handle that, but again it shouldn't happen.
|
|
#
|
|
for buildtype in std rt; do
|
|
x=`find $MY_WORKSPACE/$buildtype/results/${MY_BUILD_ENVIRONMENT_TOP}-$buildtype -name $CORRECT_OUTPUT_FILE_NAME | head -n 1`
|
|
if [ ! -z "$x" ]; then
|
|
cp $(dirname $FILE)/$CORRECT_OUTPUT_FILE_NAME $x
|
|
fi
|
|
done
|
|
|
|
echo "Have signed file $(dirname $FILE)/$CORRECT_OUTPUT_FILE_NAME"
|
|
done <<< "$OUTPUT"
|
|
|
|
\rm -f $TMPFILE
|
|
|
|
# If we just signed a shim package, flag that shim needs to be rebuilt
|
|
if [ "${TYPE}" == "shim" ]; then
|
|
REBUILD_LIST="${REBUILD_LIST} shim-signed"
|
|
fi
|
|
|
|
echo "Done"
|
|
update_signed_pkg_list ${FILE}
|
|
|
|
return 0
|
|
}
|
|
|
|
# Main script
|
|
|
|
if [ "x$MY_WORKSPACE" == "x" ]; then
|
|
echo "Environment not set up -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
ARCH="x86_64"
|
|
SIGNING_SCRIPT=/opt/signing/sign.sh
|
|
UPLOAD_PATH=`ssh $SIGNING_USER@$SIGNING_SERVER sudo $SIGNING_SCRIPT -r`
|
|
SIGNED_PKG_DB=${MY_WORKSPACE}/signed_pkg_list.txt
|
|
REBUILD_LIST=""
|
|
MY_BUILD_ENVIRONMENT_TOP=${MY_BUILD_ENVIRONMENT_TOP:-$MY_BUILD_ENVIRONMENT}
|
|
|
|
# Check that we were able to request a unique path for uploads
|
|
echo $UPLOAD_PATH | grep -q "^Upload:"
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to get upload path -- abort"
|
|
exit 1
|
|
fi
|
|
UPLOAD_PATH=`echo $UPLOAD_PATH | sed "s%^Upload: %%"`
|
|
|
|
sign_kernels
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to sign kernels -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
sign_shims
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to sign shims -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
sign_grubs
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to sign grubs -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
update_repo std
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to update std repo -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
rebuild_pkgs
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to update builds with signed dependancies -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
update_repo std
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to update std repo -- abort"
|
|
exit 1
|
|
fi
|
|
|
|
update_repo rt
|
|
if [ $? -ne 0 ]; then
|
|
echo "Failed to update rt repo -- abort"
|
|
exit 1
|
|
fi
|
|
|