SME Server:Documentation:Developers Manual:Chapter13
Packaging your application
Once you have created a customization for your SME Server by adding new files, directories, and symbolic links (for your actions, events, etc.) - and perhaps also triggering an action to initialize your customization - you are ready to package your customization into an RPM.
A quick introduction to RPMs
All SME Server software packages are distributed as RPM packages. This is the format used by CentOS and other major Linux distributions for distributing applications and other collections of files. The RPM system provides the ability to install, upgrade, remove and (importantly) verify the contents of installed packages.
An RPM essentially consists of an archive of all the files required by a piece of software. Additionally, it includes meta-information describing the software, and scripts which must be run to install or uninstall the software.
Meta-information stored in an RPM includes:
- summary and description of the software
- package name
- version number
- copyright information
- category/group to which the software belongs
- name and email address of the packager
- pre-requisites to installing this package
- ... and more
Selecting and creating RPMs for your application
Your application will typically depend on several components:
- Software packages that are shipped as a standard part of the SME Server. You do not need to include any of these packages; they are always present in the runtime environment.
- Software packages that are not a standard part of the SME Server, but that are required by your software, and would also be of general use in the runtime environment. For example: a Java runtime environment, libraries that enable communication with devices, etc. If possible, these packages should be made into separate packages, rather than being included in your application. This makes it easier to share them with other applications and enforces version compatiblity.
- Software packages that are not a standard part of the SME Server, and that are specific to your software application (i.e. not generally useful in the runtime environment). This is the raw Linux version of your application without any specific SME Server integration code. For example, if your application is already available for Linux in the form of RPMs - these RPMs are what we are referring to. These are referred to as the application RPMs.
- Any new files that you have created specifically in order to integrate your application into the SME Server runtime environment - should be packaged into a single RPM, as explained in the next section. This is referred to as the integration RPM.
So, if your application is based on Linux software that has already been packaged into RPMs, then you will need to create one new RPM:the integration RPM.
If, on the other hand, your application is based on Linux software that has not yet been packaged into RPMs, then you will probably need to create at least two RPMs: one or more application RPMs, and the integration RPM.
Finally, for simple customizations (such as the loggerdemo example earlier in this manual) there may be no application RPM at all. This would be typical if the point of the application is to change the server configuration without really adding a new software package. In this case you need only the integration RPM which contains the new template fragments, user interface screens, etc..
All files on the system, except for user data, must be installed by RPMs.
Setting up your RPM development environment
If you haven't done so already, set up an RPM development environment. If you are using an SME Server as your development environment, you will need to alter your user account to enable regular login. If you want to enable account "joe", then you would type the following commands from the root account:
chsh -s /bin/bash joe db accounts setprop joe Shell /bin/bash
Then you should be able to log in to the server as user "joe", and get a Linux command line prompt. Log in, then type the following commands to set up your RPM work area:
cd ~/ mkdir -p rpms/{SRPMS,BUILD,SOURCES,SPECS,RPMS,lib} mkdir -p rpms/RPMS/{i386,noarch} echo "%_topdir $HOME/rpms" > ~/.rpmmacros
You will now find that you have a directory called rpms in which you will do your work. Under this are the following subdirectories:
- SOURCES
- The base material from which RPMs are built -- source code, tarballs, etc.
- BUILD
- Working area used by the rpmbuild program during RPM creation
- SPECS
- Specification files for building RPMs
- SRPMS
- Source RPMS (created by build process)
- RPMS
- Binary RPMS (created by build process). Has subdirectories noarch and i386 for architecture independent and x86 platforms respectively.
As you prepare software to turn into RPMs, you will place files in these directories as appropriate. The following sections will describe what goes where as each item is covered.
Tip: As you start work on an RPM for version x.y.z of a package, create a subdirectory rpms/SOURCES/yourpackage-x.y.z/ to work in.
mkdir rpms/SOURCES/yourpackage-x.y.z
Under this directory there should be a subdirectory called root, under which is an image of the file hierarchy that will be installed by the RPM.
mkdir rpms/SOURCES/yourpackage-x.y.z/root
Building an RPM
This section describes the process for building an RPM - step by step.
Choose a name and version number for your package. We are going to package the complete loggerdemo example and will use loggerdemo and 1.0.0 as the name and version number.
Collect all of the files which have been created in the previous sections into the /tmp/ directory. There is one additional file createlinks which looks like this:
#!/usr/bin/perl -w use esmith::Build::CreateLinks qw(:all); use File::Basename; my $panel = "manager"; panel_link("loggerdemo", $panel);
Create the directory hierarchy required for building the RPM. This is very close to the hierarchy on the installed system.
# Change to the SOURCES directory cd ~/rpms/SOURCES # Remove old files (check that you don't need anything here!) rm -rf loggerdemo-1.0.0 # Create new directory mkdir loggerdemo-1.0.0 cd loggerdemo-1.0.0 # The crontab template fragment mkdir -p root/etc/e-smith/templates/etc/crontab cp /tmp/25templatedemo root/etc/e-smith/templates/etc/crontab # The web panel description mkdir -p root/etc/e-smith/web/functions cp -p /tmp/loggerdemo !$ # The web panel implementation mkdir -p root/usr/lib/perl5/site_perl/esmith/FormMagick/Panel cp -p /tmp/loggerdemo.pm !$ # The web panel English localisation mkdir -p root/etc/e-smith/locale/en-us/etc/e-smith/web/functions cp -p /tmp/loggerdemo-en !$ # The createlinks auxiliary file cp -p /tmp/createlinks . # The DB fragments should be created as files mkdir -p root/etc/e-smith/db/configuration/defaults/loggerdemo echo "service" > !$/type echo "enabled" > !$/status echo "10" > !$/Interval
Your directory structure should now look like this:
[gordonr@sevendev1 loggerdemo-1.0.0]$ find . -type f ./root/etc/e-smith/templates/etc/crontab/25templatedemo ./root/etc/e-smith/locale/en-us/etc/e-smith/web/functions/loggerdemo ./root/etc/e-smith/web/functions/loggerdemo ./root/usr/lib/perl5/site_perl/esmith/FormMagick/Panel/loggerdemo.pm ./root/etc/e-smith/db/configuration/defaults/loggerdemo/{type|status|Interval} ./createlinks
Package the directory into a tarball: cd ~/rpms/SOURCES
tar zcvf loggerdemo-1.0.0.tar.gz loggerdemo-1.0.0
Create the RPM specification "SPEC" file ~/rpms/SPECS/loggerdemo.spec which looks like this:
%define name loggerdemo %define version 1.0.0 %define release 01 Summary: SME Server logger demo Name: %{name} Version: %{version} Release: %{release} License: GPL Group: Networking/Daemons Source: %{name}-%{version}.tar.gz Packager: Fred Frog <red@example.com> BuildRoot: /var/tmp/%{name}-%{version}-%{release}-buildroot BuildArchitectures: noarch %description Logger Demo sample application. %changelog * Thu Feb 2 2006 Fred Frog <fred@example.com> - 1.0.0-01 - Original version %prep %setup %build perl createlinks %install rm -rf $RPM_BUILD_ROOT (cd root ; find . -depth -print | cpio -dump $RPM_BUILD_ROOT) rm -f %{name}-%{version}-filelist /sbin/e-smith/genfilelist $RPM_BUILD_ROOT > %{name}-%{version}-filelist %clean rm -rf $RPM_BUILD_ROOT %post /sbin/e-smith/expand-template /etc/crontab true %postun /sbin/e-smith/expand-template /etc/crontab true %files -f %{name}-%{version}-filelist %defattr(-,root,root)
Note the %post (post-installation) and %postun (post-uninstallation) statements which expand the /etc/crontab template after installing or uninstalling the RPM.
Check that your RPM will build OK with "build prepare":
cd ~/rpms/SPECS rpmbuild -bp loggerdemo.spec
The last line of output should be + exit 0 if rpmbuild is successful.
Run the rpmbuild command again to actually create your RPM with the "build all" options:
rpmbuild -ba loggerdemo.spec
If everything was successful, the last line of output should again be + exit 0.
The RPMs should have been generated and put into ~/rpms/RPMS/noarch/ as this program can run equally well on any platform. A source RPM should also exist in ~/rpms/SRPMS/.
Test your RPM by installing it on an SME Server test box.
sudo yum localinstall ~/rpms/RPMS/noarch/loggerdemo-1.0.0-01.noarch.rpm Preparing... ########################################### [100%] 1:loggerdemo ########################################### [100%] Migrating existing database mailpatterns Migrating existing database hosts Migrating existing database configuration Migrating existing database yum_repositories Migrating existing database networks Migrating existing database yum_updates Migrating existing database yum_installed Migrating existing database spamassassin Migrating existing database accounts Migrating existing database backups Migrating existing database yum_available Migrating existing database domains
The customization should be fully installed, and the /etc/crontab file should show the customization.
Then remove the customization:
sudo rpm -e loggerdemo
The customization should be completely gone, and the /etc/crontab file should look the way it did before.
The createlinks script
The source tarballs of an RPM should not include symbolic links as they are difficult to store under many version control systems and cause issues when generating patches. Since the SME Server uses many symbolic links, there are simple methods for creating the ones required. This is done through the createlinks script which is called from the %build section of the SPEC file. Let's examine one. It starts with the standard Perl script header and an import of the required module:
#!/usr/bin/perl -w use esmith::Build::CreateLinks qw(:all);
The templates2events function can be used to create the appropriate templates2expand links in various events:
my $imap = "/var/service/imap"; my $imaps = "/var/service/imaps"; templates2events("/etc/dovecot.conf", qw(bootstrap-console-save console-save)); templates2events("$imap/config", qw(bootstrap-console-save email-update)); templates2events("$imaps/config", qw(bootstrap-console-save email-update));
Note that the first argument is a filename and the second argument is a list of events in which to create the link. The safe_symlink function can be used to create a generic symbolic link, as well as the directory hierarchy enclosing that link:
for my $event (qw( email-update ldap-update network-create network-delete )) { safe_symlink("sigusr1", "root/etc/e-smith/events/$event/services2adjust/imap"); } safe_symlink("daemontools", "root/etc/rc.d/init.d/imap");
The event_link function is used to create the links from the event directories to the generic actions directory. For example:
for my $event (qw(post-upgrade)) { event_link("imap-relocate-maildirs", $event, "05"); }
creates a symbolic link S05imap-relocate-maildirs in the post-upgrade event. The target of the symbolic link will be the imap-relocate-maildirs script in the /etc/e-smith/events/actions/ directory.
Finally, the service_link_enhanced function makes it simple to create the /etc/rc.d/rc7.d and similar startup symlinks:
service_link_enhanced("imap", "S55", "7"); service_link_enhanced("imap", "K45", "6"); service_link_enhanced("imap", "K45", "0"); service_link_enhanced("imap", "K45", "1");
More documentation on this module can be seen with the command perldoc esmith::Build::CreateLinks.