Updated 8 March 2013
Linux Logo

Using rpm to Install System Scripts in Linux


Problem:

Need to create Linux Program Package to install system management shell scripts

Description:

System Managers utilize shell script programming to support their data center. These scripts are distributed to multiple servers. Scripts get change an update. There becomes a need with large server installations to have a method to easily install these scripts, identify which version and provide a standardized method for their deployment.

For Linux systems, the RedHat Package Manager (e.g. rpm) is invaluable to support this type of environemnt. The following an example for script program deployment and dispersement using rpm. Version and release can be audit by simply inquiring the Package Manager Data on each system.

Typically rpm is used with source code program that require compiling, make and installed. rpm is written with that in mine and has default build for c-code type program. This is not typically a requirement of system administrative shell or Perl scripts. Using rpm beomes somewhat eroteric to system managers with source code programming convensions that are not requirement or need for its use.

The following a simple outline to create a rpm package to install scripts (and anything else) without a lot of complexity or c-programming deployment issues. It enclose a makeRPM.sh script to build what you want. Simply modify the scripts in you script directory, then perform the makeRPM.sh to make a new package.


Breakdown

Decription

Process Definition
rpm packages are create using rpmbuild command and installed using the rpm command. These commands are documented in the man pages. How we use these commands ae summarized below:
rpmbuild -ta <script tar-gz> Used to create the package (e.g. rpm file).
rpm -ivh <package>.rpm Used to install the package
rpm -e <package> Used to remove the package
rpm -qiav | grep -i <package> Used to list the package.
What is does
The rpmbuild command is used to create an rmp install file. This file containg a cpio file consisting of all the files and directories of the software that is to be installed and where it is to be located. This file can also include any special instructs that are executed during the insrtallation.
Make Self Contain Working Directory To use rpmbuild, a directory is defined which will contain the scripts and any supporting files such as crontab entires, artwork, etc. LogiQwest q-Status server analysis utilizes a complex set of collection scripts for its web server analsysis. The scripts incorporate their own data collection directory and use an ant script, scp or ftp along with their config files to send data to the q-Status Web server. We will use these complex script environment as our example to explain how rpmbuild and rpm works with this script struture. On the other hand, a script directory could just contain a single script and maybe a README file.

The q-statusLinux working directory contains of many shell scripts, a master call script, Java jar files and libraries. All of these files and directories are to be installed one directory call q-statusLinux.

Our design is that this directory will be installed under /var/local.

Spec File The key to using rpmbuild is the definition of a spec file for a software. A spec file is a text file consist of the following sections. This is quick summary.
Header/Preamble definitions, prefixes, build locations, program name, version, revision, source, etc.
%description Program description
%prep Prepartion section
%build Build section
%install Install Section
%clean Clean up Section
%files Installed files assignments and attributes.
%post Optional for initial install
%preun Optional for removal

Major sections are defined with the keyword staring with an "%" before their name. For example "%install" defines the "install" section. The prefix "%" is also used to call variable names. Whereas in shell scripts, the entry $HOME is a variable, the entry %HOME is also the same thing in the spec fil. Strangle enough a spec file uses both conventions since they can contain regular shell scripts. Also "%" can preceed a command definition like %define. The section %build is used mostly for c-programs compile and make. It can be eliminated for our script example. %post and %preum are useful for installation but are also optional. Here is the q-statusLinux spec file:

#===============================================================================
# Copyright 2008 LogiQwest
# Name: q-statusLinux.spec 
#-------------------------------------------------------------------------------
# $Id: q-statusLinux.spec,v 1.1 2008/11/06 18:03:03 mbarto Exp $
#-------------------------------------------------------------------------------
# Purpose: RPM Spec file for q-StatusLinux 5.10
# Version 1.00:06 Nov 2008 Created.
#===============================================================================
# No debuginfo:
%define debug_package %{nil}
#%%define _topdir           /home/mbarto/rpm
#%%define _tmppath          %{_topdir}/tmp
%define name      q-statusLinux
%define summary   q-Status Linux Collections Scripts for Server Client.
%define version   5.10
%define release   Base
%define license   GPL
%define group     Applications/System
%define source    %{name}-%{version}.tar.gz
%define url       http://www.logiqwest.com
%define vendor    LogiQwest
%define packager  Michael Barto 
%define buildroot %{_tmppath}/%{name}-root
%define _prefix   /var/local
Name:      %{name}
Summary:   %{summary}
Version:   %{version}
Release:   %{release}
License:   %{license}
Group:     %{group}
Source0:   %{source}
BuildArch: noarch
Requires:  filesystem, bash, grep
Provides:  %{name}
URL:       %{url}
Buildroot: %{buildroot}
%description
Collection scripts for q-status, a Web 2.0 based server analysis and
configuration program that places at the system administrator's finger tips the
ability to display and analyze the hardware and software for their servers.
%prep
%setup -q
%build
%install
install -d %{buildroot}%{_prefix}/q-statusLinux
tar cf - . | (cd %{buildroot}%{_prefix}/q-statusLinux; tar xfp -)
rm %{buildroot}%{_prefix}/q-statusLinux/q-statusLinux.spec
# Make a cron entries files.
%{__mkdir} -p "${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.hourly"
#%%{__mkdir} -p "${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.daily"
%{__mkdir} -p "${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.weekly"
cp crons/qstatus_disks.sh ${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.hourly
#cp crons/qstatus_config.sh ${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.daily
cp crons/qstatus_config.sh ${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.weekly
%post
echo "--------------------------------------------------------"
echo "   %{name} Collection Scripts installed in %{_prefix}"
echo "   %{name} cron entries have been added to:"
echo "          %{_sysconfdir}/cron.hourly"    
echo "          %{_sysconfdir}/cron.daily"    
echo "          %{_sysconfdir}/cron.weekly"    
echo "--------------------------------------------------------"
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root)
%{_prefix}/q-statusLinux/*
# Add cron entries
%attr(755,root,root)%{_sysconfdir}/cron.hourly/qstatus_disks.sh
#%%attr(755,root,root)%{_sysconfdir}/cron.daily/qstatus_config.sh
%attr(755,root,root)%{_sysconfdir}/cron.weekly/qstatus_config.sh
How it will works In our spec file, the keyword %define allows parameters to be set and redefined. We define a %{name} parameter as follows:

%define name q-statusLinux

When rpmbuild is run, it builds our programs in a work area called %{buildroot}. In our spec file we defined this location as

%define buildroot %{_tmppath}/%{name}-root

It %buildroot depends uses the %{name} parameter with a dash extension called %{name}-root. It also uses a build in rpm global parameter called %{_tmppath}. rpm global parameters start with a "_". If you want to know what the value of a global parameter, simply use rpm with the --eval option like this:
[mbarto@localhost local]$ rpm --eval %{_tmppath}
/var/tmp
[mbarto@localhost local]$ 

In this case %{_tmppath} is "/var/tmp".

Hence our %buildroot area will be /var/tmp/q-statusLinux-root. When rpmbuild calls our spec file, it will copy our script source directory and structure to this location and will then use this %{buildroot} area to create the install struture. Do not confuse this copy as the install copy we want under /var/local. We have to actually create this in our %{buildroot} area as a separate file struture. This first directory is just to set of a repository for the script files and structure to be use to create the cpio.

Our source for our scripts is defined by:

%define source %{name}-%{version}.tar.gz

Our source code will be tar gzip file of a our source directory q-statusLinux-5.10. Therefore we are going to have a scripts and its supported file in a tar-gz file. This method is convients as it is self contained. We can make modes and changes in this directory and then us it to create our install module. As part of this working directory, the spec file q-statusLinux.spec is also placed here. When rpmbuild with the ta option, will automatically locate this file in a tar-gz file.

We also redefine the global %{_prefix}. rpm has a predefined %{_prefix} as /usr. A new definition of %{_prefix} overwrites this global with a new %define entry.

%define _prefix /var/local

In our working area, rpmbuild will build our script directory under this new prefix. But this will only happen if we tell it to do that in our spec file. Then we will install these scripts structures when we assign them in the %file section.

Header/Preamble We need to set up the Preamble for the program. Most of this stuff is put in the Package Manager Data base when we install our program. Use Use rpm -qi q-statusLinux-5.10-Base to display the values of an installed package.
[root@localhost WorkArea]# rpm -qi q-statusLinux-5.10-Base
Name        : q-statusLinux                Relocations: (not relocatable)
Version     : 5.10                              Vendor: LogiQwest
Release     : Base                          Build Date: Fri 07 Nov 2008 06:35:50 AM PST
Install Date: Fri 07 Nov 2008 06:36:40 AM PST      Build Host: localhost.localdomain
Group       : Applications/System           Source RPM: q-statusLinux-5.10-Base.src.rpm
Size        : 4553469                          License: GPL
Signature   : (none)
Packager    : Michael Barto
URL         : http://www.logiqwest.com
Summary     : q-Status Linux Collections Scripts for Server Client.
Description :
Collection scripts for q-status, a Web 2.0 based server analysis and
configuration program that places at the system administrator's finger tips the
ability to display and analyze the hardware and software for their servers.
[root@localhost WorkArea]# 

We use %{define} a value and then assign it to the preample section. This is a list of values that can be assigned and a quick definition:

Name: The short name of the package.
Summary: A short one-liner description of the software.
Version: The software version number
Release: Increment this number when doing multiple package releases of the same software version.
License: The license that the software was released under, like GPL or BSD.
Group: The package group category. This is usually for those GUI rpm installers. In this case we use Applications/System for our scripts
Source0: A reference for where the software source was obtained. Make sure your tarball name matches the name given here.
BuildArch: This is the build architecture for our rpm. In our script case, we do not care if it is i386, i486, i586, i686. So we use noarch. When rpmbuild is run, it makes the rpm modules in a directory "/usr/src/redhat/RPMS". In this directory you will find these architecture types names as directories.
Packager: The individual or organization that's creating the rpm.
Requires: For Good measure, but not really necessary.
Provides: Another useless options
Buildroot: Our root directory where the rpm build process will temporarily install the compiled software.
Some other one you can assigned.
Vendor: The maker of the software. Sometimes it's the distributor & you'll see an rpm repository name.
URL: The website for the software package developer.
Prefix: The location where the software is to be installed by the rpm. This value is used all throughout the rpm building procedure.

Defining "BuildArch" is very important since it defines where the rpm module will be created.

%description Description is pretty self explanatory. This also shows up as shown above when the package is listed with rmp.
%prep For the &prep section I use a standard macro %setup -q. This macro is responsible for the gunzip and tar xf of the source tarball into the %{buildRoot} directory. Passing the -q argument just tells it to do its work quietly. After setup is run our %{buildRoot} looks like this:

Note that setup has taken our entire q-StatusLinux-5.10 script file struture and copied it to the same directory under %{buildroot}.

%build We have no %build. We are not doing c-code.
%install This is where we assemble our file scripts scripts for deployment. This is mostly shell scripting to packages our shell script.
%install
install -d %{buildroot}%{_prefix}/q-statusLinux
tar cf - . | (cd %{buildroot}%{_prefix}/q-statusLinux; tar xfp -)
rm %{buildroot}%{_prefix}/q-statusLinux/q-statusLinux.spec

First we create the directory q-StatusLinux under /var/local in our %{buildRoot} directory using install with the "-d" option:

install -d %{buildroot}%{_prefix}/q-statusLinux.

Next we copy our script files to this directory. To do this we must understand that when a spec file is run with rmpbuild, the operations are calculated from a present work directory %{buildroot}/%{name}-%{version}, not %{buildroot}. Hence we can perform a tar copy (as I call it) to maintains all the protections and ownerships to transfer our structure to %{buildroot}/var/local/q-statusLinux.

As I said before we have a copy of the spec file in this directory. So for clean up I remove it.

rm %{buildroot}%{_prefix}/q-statusLinux/q-statusLinux.spec

so it will not be installed.

Why do you have this file there? Well when you run a rpmbuild, it is looking for a spec file and a source directory. By bundling them together, they are all in the same place and I only have to specify the tar-gzip file. It finds the spec file in the tar-gzip.

I have some other stuff there, but I will get back to that later as an option.

%post This section only display comments to the user when the rpm install is run. It is useful to me to tell the user to what to do.
%post
echo "--------------------------------------------------------"
echo "   %{name} Collection Scripts installed in %{_prefix}"
echo "   %{name} cron entries have been added to:"
echo "          %{_sysconfdir}/cron.hourly"    
echo "          %{_sysconfdir}/cron.daily"    
echo "          %{_sysconfdir}/cron.weekly"    
echo "--------------------------------------------------------"
%clean This section deletes my %{buildroot} directory after I have created the program.
%clean
rm -rf %{buildroot}
%files This is the most important section. If this is not filled in correctly, nothing will get installed. During the %install section we create a %{buildroot}%/var/local/q-statusLinux files system with all the stuff we wanted installed using parameters and scripts from the spec file. Now we want to install this. Our %files section looks like this to accomplished the install.
%files
%defattr(-,root,root)
%{_prefix}/q-statusLinux/*

%default (-, root, root) means all our files will maintain their current mode (e.g. -) and will be own by root and in the root group. The final entry %{_prefix}/q-statusLinux/* is the main secret. This means that we install from the %{buildroot}% which is in our cpio file in our rpm module the contents of "var/local/q-statusLinux/*" to /var/local/q-statusLinux. The "*" means everything in this directory including sub directories.

Using rmpbuild To use rmpbuild, first create the tar gzip file and then perform rpmbuild -ta <script tar-gz>.
[root@localhost WorkArea]# tar cf q-statusLinux-5.10.tar q-statusLinux-5.10 
[root@localhost WorkArea]# gzip q-statusLinux-5.10.tar
[root@localhost WorkArea]# rpmbuild -ta ${PROGRM_DIR}.tar.gz 
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.26915
+ umask 022
+ cd /usr/src/redhat/BUILD
+ LANG=C
+ export LANG
+ unset DISPLAY
+ cd /usr/src/redhat/BUILD
 ... ..... ...... ...... ...... ........ 
 ... ..... ...... ...... ...... ........ 
 ... ..... ...... ...... ...... ........ 
 ... ..... ...... ...... ...... ........ 
 ... ..... ...... ...... ...... ........ 
Processing files: q-statusLinux-5.10-Base
Provides: q-statusLinux
Requires(interp): /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh
Requires: /bin/sh /usr/bin/perl /usr/bin/python bash filesystem grep perl(strict)
Checking for unpackaged file(s): /usr/lib/rpm/check-files /var/tmp/q-statusLinux-root
Wrote: /usr/src/redhat/SRPMS/q-statusLinux-5.10-Base.src.rpm
Wrote: /usr/src/redhat/RPMS/noarch/q-statusLinux-5.10-Base.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.27780
+ umask 022
+ cd /usr/src/redhat/BUILD
+ cd q-statusLinux-5.10
+ rm -rf /var/tmp/q-statusLinux-root
+ exit 0

[root@localhost WorkArea]# 

Because we defined a BuildArch of noarch, our rmp module is create and named at:

/usr/src/redhat/RPMS/noarch/q-statusLinux-5.10-Base.noarch.rpm

Installing the package module

To installed this new package use rpm -ivh q-statusLinux-5.10-Base.noarch.rpm.

[root@localhost WorkArea]# rpm -ihv q-statusLinux-5.10-Base.noarch.rpm
Preparing...                ########################################### [100%]
   1:q-statusLinux          ########################################### [100%]
--------------------------------------------------------
   q-statusLinux Collection Scripts installed in /var/local
   q-statusLinux cron entries have been added to:
          /etc/cron.hourly
          /etc/cron.daily
          /etc/cron.weekly
--------------------------------------------------------
[root@localhost WorkArea]# 

As you see some notes are displayed from the %post section. The scripts are now in /var/local.

Additional installs We skip the contab installs in he first pass of the spec file. The cron files are located in the q-statusLinux-5.10 working directory in a directory called cron. The files need to be installed in a different location than /var/local. rpmbuild has a global parameter called %{_sysconfdir}. This is defined as "/etc". In the %install section, we copy these files to this global directory:
# Make a cron entries files.
%{__mkdir} -p "${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.hourly"
#%%{__mkdir} -p "${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.daily"
%{__mkdir} -p "${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.weekly"
cp crons/qstatus_disks.sh ${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.hourly
#cp crons/qstatus_config.sh ${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.daily
cp crons/qstatus_config.sh ${RPM_BUILD_ROOT}/%{_sysconfdir}/cron.weekly

In the %file section, we assign the installation:

# Add cron entries 
%attr(755,root,root)%{_sysconfdir}/cron.hourly/qstatus_disks.sh
#%%attr(755,root,root)%{_sysconfdir}/cron.daily/qstatus_config.sh
%attr(755,root,root)%{_sysconfdir}/cron.weekly/qstatus_config.sh

So when the program is installed it adds the cron entries.

Why? LogiQwest has documented this to aid system managers to use this template to support more than just installing q-statusLinux collection scripts. For our q-Status™ Server Analysis product, certain parameter files for our program need to be added such as a the q-StatusLinux server name, userid and passwd for ANT jsch data copy in our scripting directory by the client. Once these scripts are customized, a simple make script creates a new rmp module which can be distributed to many server. This is a little more automated than this discussion with q-Status, but the method is the same. Also, there are lot of other things we left out. But that can be Google. By the way %setup has some other options besides "-q". You can use define you own creation directory while you are testing instead of using /usr/src/redhat. The %file section has other options also. Anyway, we hope it gets you started and comments and suggestion are welcome. Simply contact me Michael Barto at mbarto@logiqwest.com

Click for more info