CTS 2322 (Unix/Linux Administration II) Project
Building and Installing Software from Source Code


Due: by the start of class on the date shown on the syllabus


In this project, you will download and install a source code RPM package.  Next you will download and install a (standard) tar-ball.  You will then build binary packages from both and install them.  Finally, you will setup a local yum repository with your package and install it using dnf.

The version of the pine package used (version 4.64) won't install by default on a “vanilla” Fedora system.  You will have to trouble-shoot the install to make this work!


Answer the following questions and perform the following tasks:

Part I — Installing pine from a source RPM:

  1. Fedora no longer includes the pine MUA (email program).  What is the home URL for pine?  (Hint:  pine was maintained by the University of Washington.)
  2. Since a source RPM for pine is hard to find online, here are links for the package and some patch files: pine-4.64-1.src.rpm and pine-4.64-maildir.patch.gz.  For now, just download the RPM package.

    Note that “pine” is a discontinued project.  The replacement for pine is “alpine” but for this project you must use “pine” and not “alpine”! You can get patches for pine and alpine, from alpine.x10host.com.

  3. If you don't have the rpmbuild command on your system, use what you have learned to locate the package for it and install that.  (You may need to install additional packages if you need to use some command that isn't currently installed on your system.)
  4. Once RPM tools are installed, you need to create a build environment in your home directory (your non-root account).  You do this by running the command “rpmdev-setuptree”.  What files and directories were created in your home directory by this command?
  5. Install the source package (as you, not as root) using the “rpm -U” command or the “dnf install” command.  This will create a source tar-ball and a “spec” file somewhere under ~/rpmbuild/).  What is the exact command you used for this?  What files were installed?  (List the complete pathnames; using tree is helpful.)

    You can ignore any warning you get about missing users or groups.  That happens because the original developer was too lazy to run the chown command on the files before packaging them up!  The system will default to using root user and group.

  6. Next, cd into the SPECS sub-directory and attempt to build a binary RPM from the installed source using the command “rpmbuild -bb spec-file”.  For the version of the pine source RPM used (version 4.64), this won't work on Fedora!  Now troubleshoot the problems and fix them:
    1. What error message is produced?  The error message includes the line number from the “spec” file that rpmbuild didn't like.  Before making any changes to the spec file, make a copy of it.
    2. Try to fix this problem by editing the “spec” file and commenting out the offending line.  Do this by pre-pending (adding to the front of the line) the “#” character, which is the start of comment character.
    3. Now try the rpmbuild command again.  What is the error this time?
    4. Run the rpmlint -i command on the spec file and note the error(s) it shows.  You can figure out what is wrong only by knowing the “spec” file syntax.  This can be found at www.rpm.org.  However, in the interest of saving you some time I will provide these hints:  Older versions of RPM required a “Copyright:” line in the “spec” file, but modern versions have replaced that with a “License:” line.  In addition, the list of valid RPM package “groups” for Fedora has changed.  The group listed here has nothing to do with package groups, so Fedora (and other distros) have deprecated the group tag.  You should either delete the line, or use “Unspecified” as the group if you must have the tag.
    5. Edit the “spec” file and eliminate all the errors shown with rpmlint.  (It is okay to ignore warnings.)  What change(s) did you make to the “spec” file?  (Show the output of diff with the original and modified versions.)
    6. Try building the pine binary RPM.  You may find a warning about how OpenSSL can't be found.  (That's because the DLL file for it has changed its name over the years.)  You can continue the build anyway without SSL (the current OpenSSL version is not compatible with the version that pine expects).  (See the hints section below.)  If the build is successful, the result is a binary RPM.
    7. Even once the spec file is correct, the build may not succeed due to missing libraries or other files.   Or you may discover missing development tools.  These problems can be solved one at a time, or you could use the group install feature of dnf, to install all the development tools, C-development tools, and all development libraries at once.  Then you can try the build again.

      What packages and/or package groups did you need to install?

      If “cc” is used and not found, you can fix that by adding a symlink from gcc to cc, or by installing the ccache package (on Fedora anyway), or by editing the spec file and changing the build line by overriding the CC setting (shown in bold):

      ./build lrh CC=gcc

      What (if anything) did you need to change in the spec file?

      If the build still fails, examine the output in top-down order to find the error that caused the build to fail.  (You are looking for the first compiler error, not simply warnings that include the word error.What was the first error?

      On a Fedora 21 system, the error was an undefined symbol.  Furthermore, the error included a helpful message about which library does include the missing stuff.  To include that library during the build, you should export the environment variable “LDFLAGS”, set to the name of the missing library.  For example, if the missing library is “/usr/lib/libfoo-3.5.so”, then set and export the environment variable like this: “export LDFLAGS='-lfoo'”.

      Since Fedora 27 I've had a different error.  The C compiler complained that:

      ../pico/os.h:65:16: error: 'SIGUNUSED' undeclared (first use in this function);
                          did you mean 'SI_USER'?
       #define SIGEMT SIGUNUSED

      No extra library will help with this; the symbol was defined 15 or 20 years ago, but is not standard any longer and no longer defined in any file.  Is all hope of building Pine lost?  No, because you can define it on the compiler's command line.  The man page for signal(7) still documents that as having the value “31”.  Passing C compiler options is much like passing linker options:


      Once you set this correctly, you should be able to successfully build the binary pine RPM.  (Note that in general, such an issue would not be a system admin's responsibility but rather a developer issue.  So this part of the project is somewhat unrealistic.)  What was the environment variable setting(s) or other changes required to successfully build the pine RPM?

      Instead of setting environment variables, you could modify the spec file to include the extra option(s).  Replace the lines:

          ./build lrh



      (The “NOSSL” says to not even try to use SSL.)

  7. As root, install the binary RPM you built for pine.  (You may have to uninstall alpine first.  When done with this project, remember to re-install alpine if you plain on using it!)  What is the exact command(s) used?  Check to see if the install worked, by running pine, pico, and viewing the man pages.
  8. By default, pine and Alpine use MBOX format.  However there is an available patch to pine and for alpine to enable support “Maildir” format as well.  Use the link provided above, download and then apply the maildir patch to the pine source, and then rebuild.  Verify the patch file downloaded correctly, by comparing its MD5 checksum to “f8eac0f0d741bd2096ec580b217bcb5b”.  (If it doesn't match, try downloading the patch file using wget from the command line.)

    You could apply the patch to the extracted pine source; use “tar -xf pine*” to extract the archive, then you can apply the patch.  A simpler and better way would be to put the patch file(s) in the SOURCES directory, add some lines to the spec file, and have rpmbuild patch the source for you.  Look in the spec documentation for “Patch0:” and “%Patch0” spec file lines.  What did you do to apply the patch?

    (If you wish to install and use the modified Pine, you will need to first uninstall the pine RPM that is already installed.  The reason is, both packages have the same name and version, so you cannot update the old with the new.)

Part II — Building from source:

  1. Download the tar-ball myhello-1.0.tgz from this website.  (This is a simplified version of Gnu hello.)  Now unpack, compile, and install this tar-ball:
    1. What is the MD5 checksum for this tar-ball?  Does it agree with the following?
      bb3dc635c0023967e46f35130e96cde6  myhello-1.0.tgz

      If the checksums do not match, the download corrupted the file.  If you click on the file link in your web browser to save the file, it turns out that it gzips the file (which was already gzipped)!  This seems to be an old bug in Firefox.

      In any case, if you right-click on the file name and copy the URL of the file (“Copy Link Location”) you can safely download the file from the command line using curl or wget.  For example:

      wget https://wpollock.com/AUnix2/myhello-1.0.tgz
    2. Unpack the tar-ball using tarWhat is the exact command you used?
    3. Change your current working directory to myhello-1.0 and read the various installation documentation provided.  What files did you (or should you) look at before installing?
    4. Compile and install the software.  (Hint:  As this is a standard Gnu package, you use the standard steps to configure, compile, and install the software.) Which one of these commands is the only one that should be run as root?  What are the exact commands you used?
  2. Check your installation by running the command hello and reading the hello(1) man page.  What files were installed (and where)?  What would you have needed to do, to have the files install under “/opt/” instead?
  3. Now clean up the build files.  One way is to simply delete the entire build directory.  However, the Makefile generated usually has targets to clean up the files, while leaving the configuration files.  Examine the MakefileWhat “make” command can you use to delete the build files but not the configuration files?  What other targets look interesting/useful to you?
  4. Change to your home directory, and delete the entire build directory (“myhello-1.0/”).  As root, delete all other files installed.  When you install from source, there is no record (except in your system journal) of what files were installed.  If you didn't have an accurate system journal, what could you do to determine what files were installed by some source (tar-ball) installation?

Part III — Building a signed binary RPM from a tar-ball:

Next build a digitally-signed, binary RPM package from the myhello tar-ball.  If you are lucky, a tar-ball will include the “spec” file required to build an RPM package.  Sadly for you, this is not the case here.  So you will need to create a spec file.  But first, you must create a gpg key to use to sign the package(s) and the repository meta-data.

  1. Generate a GPG key-pair, to be used to digitally sign your package:
    $ gpg --full-generate-key --keyid-format short # and follow the prompts

    You should use the default key type and size.  Have your key expire in 4 months (long enough to last to the end of the term).  Use your real name, localhost email address (or alias), and a comment similar to “Software Packager for MyLocalRepo”.  Pick a good passphrase, and record it someplace (not in your journal).  What was the gpg output?  What is the public key's ID (the eight hex digits after the size and type)?  Where did you record the key info and password?

  2. Set up the RPM build system to support signing RPMs.  You should have set up the basic build environment previously, in part I of this assignment.  Now you should edit (or create if missing) the file “~/.rpmmacros”, to contain the information needed, and to set default values, used by the rpmbuild tool.  Here is a copy of the one I use (the three lines I added to the default file are shown in boldface):
    %_topdir %(echo $HOME)/rpmbuild
    %packager       Wayne Pollock <wpollock@example.com>
    %_signature     gpg
    %_gpg_name      9F19C168
    %__arch_install_post \
        [ "%{buildarch}" = "noarch" ] || QA_CHECK_RPATHS=1 ; \
        case "${QA_CHECK_RPATHS:-}" in [1yY]*) /usr/lib/rpm/check-rpaths ;; esac \

    (Only the “%packager” line needs to be added, in general.  The other two added lines are to sign the resulting packages using the correct GPG key.  The rest are settings used to speed the compiles and run appropriate cleanup and post-build utilities.)  Be sure to edit this file, changing the information to your own name, GPG key, and home directory.  What are the contents of your ~/.rpmmacros file?

  3. Copy the tar-ball to ~/rpmbuild/SOURCES/myhello-1.0.tgz.
  4. Next, cd into the ~/rpmbuild/SPECS directory and create a spec file here, named “myhello-1.0.spec”.  This is the hard part!  You could create a template spec file with the command rpmdev-newspec.  An interesting feature of vim is that when you create a file with a name of the form “name-version.spec”, it will create a template you can easily edit.  Try this now and look through the result.
  5. While a cruel instructor would make you read the tutorial and create your own spec file, you can just use this one:
    Name:      myhello
    Version:   1.0
    Release:   1%{?dist}
    Summary:   Copy of Gnu hello
    License:   GPLv2
    URL:       http://www.gnu.org/software/hello/
    Source:    https://wpollock.com/AUnix2/myhello-1.0.tgz
    Prefix:    /usr/local
    BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
    Gnu hello is a sample project showing the Gnu build tool-chain.
    %setup -q
    make %{?_smp_mflags}
    rm -rf $RPM_BUILD_ROOT
    make install DESTDIR=$RPM_BUILD_ROOT
    rm -rf $RPM_BUILD_ROOT
    %doc %{_mandir}/man1/hello.1.gz
    * Thu Jun 08 2018 Wayne Pollock <pollock@acm.org> - 1.0-1
    - Updated spec file to match current RPM spec format

    The parts that need to be filled in are: Name, Version, Summary, URL (which is optional; here I've listed the GNU source URL), and Source.  (While you could just list a tar-ball for source, without a URL rpmlint will complain.  The URL doesn't have to actually work; only the file name part is used.)  Then you need to add a description (can be multiple lines) after the “%description” line.  The last part you would need to edit is the list of files to be installed by this package (the lines following the “%defattr(-,root,root)” line).  These are the ones created when you did the make install step previously.  Note how documentation files are marked with “%doc”.  What is the tag you would use to mark configuration files?  (You will probably need to do a bit of searching in the tutorials to find this.)

  6. Run rpmlint on your modified spec file, and fix any errors reported.  (It is expected that the value for Source: will generate an illegal URL most of the time.  This is safe to ignore in this case.)

    You might see a warning hardcoded-prefix-tag /usr/local; that's a bogus warning so ignore it.  (If you do remove the Prefix: tag, the resulting package is not relocatable; with the tag, it is.)  I suspect the warning is to prevent relocatable packages, which Red Hat discourages.

    What errors did you fix?  What warnings did you see?

  7. Now build a binary RPM package using the rpmbuild command, with the correct option.  What is the exact command you used?

    If this works, there will be a lot of debugging output, ending with a line “+ exit 0” to indicate the build was successful.  (An exit status other than zero would indicate a failure.)  If this failed to work, you can use the rpmlint command on your spec file to see what might be the problem.  (You may need to install an additional package for this tool.)

    Locate the resulting RPM package.  Where did the system put the resulting RPM (the full pathname)?  What is the exact name of the created package?

  8. Now cd into the directory containing your new package.  Sign your freshly built .rpm file using the command “rpmsign --addsign NameOfPackage.rpm”.  (This won't work unless you (a) install the signing package, (b) generate an appropriate GPG signing key, and (c) use the correct key ID in your ~/.rpmmacros file.)

    The signing of RPMs is done by either the rpmsign command, or a plug-in to the rpmbuild --sign command.  On Fedora this package is not installed by default.  Install the rpm-sign package first, or the rpmbuild --sign command will silently skip signing the package, and the rpmsign command won't be found.

    If you don't run this command as the same user that created the GPG key, then the key won't be found.  (My students make a habit of running commands as root when they shouldn't!)  You can export GNUPGHOME=~user/.gnupg to tell the command where to find your keyrings.  (Use your username for user, of course.)

  9. Use the rpm command to list the package information, and to list the files provided by this package.  (Note you will need to add the “-p” option in addition to the normal query options, to specify an un-installed .rpm file to query.)  What (two) rpm commands did you use?  In the rpm output, what shows the package was correctly signed and who was the packager?
  10. As root, attempt to install this package using the command “rpm -Uvh”.  (If you have SE Linux set to “enforcing”, this may not work.  Troubleshoot the problem and include the extra steps in your submission.)  What change(s) did you make, if any?  What was the result of running the rpm command?  Try running the hello command, and examine the man page installed. What was the output of running the command?  Were you able to view the man page successfully?

Part IV — Creating and Using a yum Repository:

  1. Remove the package you installed, using rpm -e.  Verify the command hello no longer works and the man page is gone.
  2. Next, create a new directory for your local yum repo.  This can be in any storage volume with sufficient space and appropriate mount options.  What is the pathname to the root of your new repository?  (For the remaining steps of this project, I will assume a pathname of “/var/myrepo/”.)  Make sure your new directory (and all its parents) have the correct permissions (searchable and readable by everyone).
  3. Copy your rpm package created in part III into your repository.  Make sure the file is read-only (by everyone).  Then initialize the repo, by creating the appropriate meta-data files, using the command
    createrepo /var/myrepo

    (You may have to install some package to have this command.)  What was the output of running this command?  What new files were created in your repo directory?  Your repo files must be readable (and the directories searchable) by all users.  This may or may not be the case, depending on your umask setting.  If necessary, adjust the permissions.

    To specify package groups, you need an extra file and option.  See fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups for details.

  4. To allow clients who do a “dnf install” to validate the repo's digital signature, you should sign the repo meta-data file.  This is rarely the case; mostly, just the packages are signed.  But you can do this just for practice.  Put the public key in the root directory of your repo (it doesn't have to be located here, but this is convenient):
    $ cd /var/myrepo
    $ export GNUPGHOME=~/.gnupg
    $ su -c 'gpg --detach-sign --armor repodata/repomd.xml'
    You need a passphrase to unlock the secret key for
    user: "Wayne Pollock (Software Packager for MyLocalRepository) <wpollock@localhost>"
    2048-bit RSA key, ID 9F19C168, created 2012-05-21
    Enter passphrase:
    $ su -c 'gpg --export -armor "Software Packager" > /var/myrepo/RPM-GPG-KEY-myrepokey.asc'

    (Adjust permissions if needed, so everyone has read access to all files in your repo.)

    Use su and not sudo, since sudo resets the environment (unless configured differently), and gpg will use root's keyring, not yours.  This can be addressed by adding the gpg option of --homedir ~ua00/.gnupg.  Using su (if GNUPGHOME is already exported in a login script) may be easier.  With either method, gpg will complain about “unsafe ownership on homedir”, since the user running gpg is different than the owner of the directory.  This is nothing to worry about.

  5. To use this repo from other hosts, you would need to configure a web server or FTP server.  For this project, we will just access the repo locally.  (In the real world this is often the case; you use NFS to remotely mount the repo on your hosts.)

    Configure dnf to know about your new repo, by creating a new file in /etc/yum.repos.d/.  See the man page for yum.conf(5) for the syntax and all the available options for this file.  At a minimum, your file must contain a unique repository ID, a human-friendly description, and the URL to your repo:

    name=Local RPM repo for my company

    (Some options you should consider including are enabled=1, gpgcheck=0 (if you didn't digitally sign your RPMs), gpgcheck=1 and gpgkey=URL (if you did sign them), and possibly others.  You can examine the other files, or read the man page, to see what directives can be used.

    What is the name and contents of your repo file?

  6. Now install your package using the dnf command.  What was the exact command line you used, and what was the output?  Verify the hello command from the package installed correctly.  (Note, if your repo is disabled by default, you will need to use the dnf command line option to enable your repo.)
  7. Update your system journal showing all changes made to your system.


Trouble-shooting Compiles

To Be Turned In:

Answers to the above questions and the relevant system journal entries.  Use Canvas and submit to the project's drop-box. 

When done, do not delete your new repo!  You will use it again in a future project.

Please see your syllabus for more information about submitting projects.