SME Server:Documentation:Developers Manual:Section3

From SME Server
Jump to navigation Jump to search

III. How to create an SME Server package - step by step

Getting started

The best way to get started is to install an SME Server and start experimenting with it. Download a copy from SME_Server:Download and burn your own CD.

If you (or any developers at your organization) have multiple computers on a home network, a cablemodem, DSL, or dialup connection, and an old Pentium machine that you don't need, we recommend installing the SME Server software on the old Pentium machine, and using it as a home gateway and firewall.

Important.png Note:
The SME Server software erases all data from the PC on which it is installed, to turn it into a dedicated server that can run 24x7. Do not install it on a PC unless you are prepared to erase all of its data!

Alternatively, you can install the SME Server on a corporate LAN in server/gateway mode (creating a small private network behind a firewall that occupies a single IP address on the Internet) or in server-only mode - in which the SME Server provides network services to other computers as a peer on the network.


Warning.png Warning:
The server-only mode is designed for LAN environments that already have a firewall/gateway.

In addition to feeling comfortable installing and using the SME Server software, you should also have a working knowledge of Linux, including use of the command line tools.

You should also be familiar with the perl programming language. Most of the SME Server software is written in perl, and the configuration template mechanism is based on perl.

It is strongly recommended that you obtain and read a copy of the book Maximum RPM (ISBN 067231 1054) or study the on-line version available at http://www.rpm.org/max-rpm/.

You also need to know how to use one of the Linux text editors such as vi, nano or pico. It is also possible to edit files on a remote machine and copy them to the server. However, it is important that the files are converted to Unix text format.


Creating a development environment

Packages which do not require compilation, for example shell and perl scripts, can be built on the SME Server platform. All of the examples in this documentation can be performed on a standard SME Server installation.

Before attempting to compile any software, you should check whether the package is available from one of the many well-maintained RPM repositories. Using these RPMs will ensure compatibility with the other RPMs on the SME Server. You are likely to find the package you want in either the CentOS or Dag Wieers repositories.

If an RPM does not already exist, you should install a CentOS developer workstation or server for SME Server development. You will also need to install the e-smith-devtools packages which can be found on the SME Server CD.

Important.png Note:
We strongly recommend against installing development tools, such as compilers, on any production servers, especially those which are accessible from the Internet.

Getting to know how to customize the SME Server

Once you have studied the architecture of the SME Server, it is best to try to make some small customizations to become comfortable with the concepts. The number one rule to remember is: customizations always involve adding files to the server, rather than modifying existing files. This is very important, as it enables customizations to be easily packaged, and mixed and matched. The unique architecture of the SME Server enables virtually anything to be customized by adding a file in the correct location.


Exercise 1: Changing a configuration template

Let us say that you wish to customize your server so that it runs a specified program every twenty minutes. To simplify the problem, let us assume that this program simply adds a line of dots to the log file (/var/log/messages), i.e.:

/usr/bin/logger -t "Demo" "......"

Normally you would accomplish this by adding a line to the /etc/crontab file, which is the standard Linux mechanism for running scheduled jobs. However, the default /etc/crontab file looks something like this on an SME Server:

#------------------------------------------------------------
 #              !!DO NOT MODIFY THIS FILE!!
 #
 # Manual changes will be lost when this file is regenerated.
 #
 # Please read the developer's guide, which is available
 # at http://www.contribs.org/development/
 #
 # Copyright (C) 1999-2006 Mitel Networks Corporation
 #------------------------------------------------------------
 
 SHELL=/bin/bash
 PATH=/sbin:/bin:/usr/sbin:/usr/bin
 MAILTO=root
 
 # run-parts
 
 01 * * * * root run-parts /etc/cron.hourly
 02 4 * * * root run-parts /etc/cron.daily
 22 4 * * 0 root run-parts /etc/cron.weekly
 42 4 1 * * root run-parts /etc/cron.monthly
 
 # logrotate
 12 1 */7 * * root       /sbin/e-smith/signal-event logrotate

Note the auto-generated comment block which reminds you not to edit the file. If you do, your changes will be overwritten when the template is next expanded by a system event. We want to append a new line that looks like this (read the Linux crontab documentation to understand the format of crontab entries):

*/20  *  *  *  *   root   /usr/bin/logger -t "Demo" "......"

Remember that we cannot simply edit the /etc/crontab file. The rule is that we must perform this customization by adding a new file to the system. To get an idea how to do this, have a look at the contents of the template for /etc/crontab:

[gordonr@smebuild crontab]$ pwd
/etc/e-smith/templates/etc/crontab

[gordonr@smebuild crontab]$ ls
00setup  10runparts  20statusreport  65_logrotate  email

Each of the files in that directory is a template fragment. The SME Server builds the /etc/crontab file by assembling those fragments and running them through the template processor.

To make your customization, create your own additional fragment by creating a file in this directory called 25templatedemo with the following contents:

# Template demo crontab entry:
 */20  *  *  *  *   root   /usr/bin/logger -t "Demo" "......"

Next time the SME Server regenerates the /etc/crontab file, it will contain your additional fragment. Starting the name with the prefix "25" forces the template fragment to go between the "20statusreport" and "65_logrotate" fragments. Force the /etc/crontab file to be generated immediately by typing the command:

expand-template /etc/crontab

If you look at the /etc/crontab file now, you should see your new fragment at the appropriate place, and your customization will take effect immediately (as cron notices when its configuration file has been changed). Check /var/log/messages to see the results.

To package this customization, you will need to create an RPM package that contains this single file, and ensures that the /etc/crontab template is expanded in the relevant events. You should also call one of these events in the RPM post-install section to ensure that the template is expanded without further action. Installing that RPM on any SME Server will cause the customization to occur, and will start printing the line of dots to the /var/log/messages logfile every 20 minutes.

The final point to note here is that if you remove your new file 25templatedemo and re-expand the /etc/crontab template, the crontab will go back to the way it was, and your customization will disappear cleanly. Therefore you should put a post-uninstallation script into your RPM package that runs the appropriate events to expand the templates once more. That will result in a package that installs and uninstalls cleanly.

Remember that for testing you can call expand-template directly in the post-install and post-uninstall sections, but released software should use the templates2expand mechanism to request template expansion in the relevant events.


Exercise 2: The magic of templates

For the next exercise, let us build on the first one. You have already created an exciting (o.k. not that exciting) new capability - the ability of the server to write dots into the log file. Let us now take advantage of the fact that the template processor can fill in values from the configuration database.

Edit the /etc/e-smith/templates/etc/crontab/25templatedemo file again, this time with the following contents:

# Template demo crontab entry:
 */20 * * * * root /usr/bin/logger -t "Demo3" "... {
   use esmith::AccountsDB;
 
   $adb = esmith::AccountsDB->open_ro;
   $adb->get_prop('admin', 'ForwardAddress') || 'admin';
 }

Once again, regenerate the template by typing:

expand-template /etc/crontab

If you look at the new /etc/crontab file, you will see that the template processor has replaced the block between the braces with the actual email address of the administrator, which is defined in the accounts database.

Important.png Note:
Be careful with the placement of the braces in the example. We want two lines of output - the comment and the line starting with the asterisk. If you move the opening brace onto a new line, you will end up with three lines of output.

Whitespace is not signifcant and the code within braces should be formatted "nicely". However, whitespace outside braces is significant and will be copied literally to the output file.


You could check that value with the db accounts show admin command. With this change to the template, the message which will go to the /var/log/messages log file every 20 minutes will contain the actual current administrative email address, rather than a hardcoded text message.

Now here is the exciting part. Click on the E-mail function in the server manager user interface, and change the administrative email address. If you look at /etc/crontab, you will see that it has been updated with the new email address! Every 20 minutes you will receive a new entry in the messages log which automatically reflects the new setting of the administrator email address.

So by simply creating a single file 25templatedemo, you have created quite a sophisticated customization that changes behaviour based on the system settings. And you have done so without affecting the rest of the system or requiring additional changes to the user interface.

The reason this works is subtle, but follows from the overall architecture. It is critical to study this example until you understand it thoroughly. If you understand exactly how this example works, you understand a substantial part of the SME Server architecture.


Important.png Note:
Please note that as of SME Server version 9 and forward, the default location of the esmith perl libraries is'/usr/share/perl5/vendor_perl/esmith, for previous SME Server versions is '/usr/lib/perl5/site_perl/esmith'.

To understand this example in detail, let's start from the top by studying the user interface. The implementation section of the E-mail function can be found in the file /usr/lib/perl5/site_perl/esmith/FormMagick/Panel/emailsettings.pm. If you study the part of this script that gets executed when the user clicks Save, you will find some Perl code that saves the administrator e-mail address into the configuration database and signals the email-update event - thus informing the system that the email settings have changed:

sub change_settings_delivery
 {
     my ($fm) = @_;
     my $q = $fm->{'cgi'};
 
     [...]
 
     my $admin_email = $q->param('AdminEmail');
     my $admin = $accounts->get('admin');
     if ($admin_email)
     {
         $admin->merge_props(
                 EmailForward => 'forward',
                 ForwardAddress => $admin_email,
                 );
     }
     else
     {
         $admin->merge_props(
                 EmailForward => 'local',
                 ForwardAddress => '',
                 );
     }
 
     [...]
 
     unless ( system( "/sbin/e-smith/signal-event", "email-update" ) == 0 )
     {
         $fm->error('ERROR_UPDATING');
         return undef;
     }
     $fm->success('SUCCESS');
 }

When the email-update event is signalled, the SME Server executes all of the action scripts in the /etc/e-smith/events/email-update/ directory. The event also expands all templates as noted in the templates2expand/ hierarchy, including the /etc/crontab template.

That may all seem rather complicated, but it boils down to this: changing the administrator email address automatically rebuilds the /etc/crontab template - and if you have customized that template, your customization will automatically pick up the current administrator email address.

Note that if you wanted additional programs to execute whenever the email settings were changed, you would put all of those programs in the /etc/e-smith/events/actions/ directory, then create symbolic links to them from the /etc/e-smith/events/email-update/ directory. We use symbolic links because you may want your program triggered by other events as well as email-update, and so you create links from all of the relevant event directories back to your action program.

This system is, by design, extensible. For example, you could use this exact same type of customization to send an email message every hour containing the current IP address. This is left as an exercise to the reader.


Exercise 3: Using events and actions

In the SME Server, events are like callbacks in a programming language. The system signals an event whenever something interesting happens (e.g. a user is added, the IP address changes, etc.), which automatically executes all programs in the event directory. Therefore, any applications which need to know when a certain event is happening simply create a symbolic link from the event directory to a handler program, which will get executed whenever the event occurs.

In the previous exercise, we relied on the predefined events and actions in the SME Server to keep the /etc/crontab file up to date. In this example, we will create a new action script that will track the user accounts in the system. This script can be used as a template for any type of application that has its own notion of user accounts and needs to be driven by the existing user interface for adding, deleting, or modifying users.

Start by creating a new file called /etc/e-smith/events/actions/demo-user-tracking with the following contents:

#!/usr/bin/perl -w
 
 # Set up Perl environment and libraries
 
 package esmith;
 use strict;
 use warnings;
 use esmith::ConfigDB;
 use esmith::AccountsDB;
 
 # Prepare to access configuration databases
 my $db = esmith::ConfigDB->open_ro
     or die "Couldn't open ConfigDB\n";
 
 my $accounts = esmith::AccountsDB->open_ro
     or die "Couldn't open AccountsDB\n";
 
 # Read domain name from configuration database
 my $domain = $db->get_value('DomainName');
 
 # Read command line arguments
 my $event = $ARGV [0];
 my $id    = $ARGV [1];
 
 # If no command line arguments, assume this is the initial setup
  # of all users. Process all accounts of type "user" (ignore groups,
 # information bays, printers, etc.)
 
 unless ($event and $id)
 {
     for my $user ($accounts->users)
     {
         my $key     = $user->key;
         my $first   = $user->prop('FirstName');
         my $last    = $user->prop('LastName');
 
         system ("/usr/bin/logger", "-t", "Demo3",
                 "Initializing user $key ($first $last) in domain $domain");
     }
 
     exit 0;
 }
 
 # If command line arguments are present, then this is a create, modify,
 # or delete event, signalled by the SME Server event/action system.
 
 my $user  = $accounts->get($id)
     or die "User $id does not exist\n";
 
 my $first = $user->prop('FirstName');
 my $last  = $user->prop('LastName');
 
 if ($event eq 'user-create')
 {
     system ("/usr/bin/logger", "-t", "Demo3",
             "Creating user $id ($first $last) in domain $domain");
 }
 elsif ($event eq 'user-modify')
 {
     system ("/usr/bin/logger", "-t", "Demo3",
             "Changing user $id to ($first $last) in domain $domain");
 }
 elsif ($event eq 'user-delete')
 {
     system ("/usr/bin/logger", "-t", "Demo3",
             "Deleting user $id in domain $domain");
 }
 else
 {
     system ("/usr/bin/logger", "-t", "Demo3", "Ignoring $event event");
 }
 
 exit 0;

Make sure the permissions are correct:

chmod +x /etc/e-smith/events/actions/demo-user-tracking

Now create symbolic links so that this program is executed whenever a user is created, modified, or deleted. Make three symbolic links; one for each event directory:

cd /etc/e-smith/events
ln -s ../actions/demo-user-tracking user-create/S90demo-user-tracking
ln -s ../actions/demo-user-tracking user-modify/S90demo-user-tracking
ln -s ../actions/demo-user-tracking user-delete/S90demo-user-tracking

The S90 prefix ensures that the program will be executed after the standard actions (which typically have prefixes ranging from S15 to S80).

Study this program carefully. It uses many different SME Server capabilities. If invoked from the command line with no arguments, it will read all user accounts from the user database, fetch all the data fields associated with each user, and print this information to the log file. If invoked as an event, the SME Server will automatically pass it the event name and user id as command line arguments; in this case the program will print messages to the log file explaining that it is adding, modifying, or dropping the user.

Trying watching the log file by running the command:

tail -F /var/log/messages

or with the "View log files" panel in the server-manager. Use the standard SME Server user interface to add, modify, or remove users. You should see a stream of comments in the log file.

If you were creating an application that had its own database of user information, you would replace the logfile-writing code with your own code that initialized new users, modified them, etc. Then you would arrange for the RPM post-install script to execute:

/etc/e-smith/events/actions/demo-user-tracking

with no command line arguments. When the application is initially installed, it will immediately read all users from the database and set them up in your application. Then, as users are added, modified, or dropped over time - your code will be invoked each time, to update the application's private user database.

Tip: It is almost always better to extend the existing accounts database with additional properties for your application than to maintain another database.


Exercise 4: Adding new configuration database parameters

New system configuration parameters can be spontaneously invented and added to the configuration database at any time. For example, let us return to our earlier exercise and parameterize the time interval for the log messages by introducing a new parameter called LogInterval.

You can write that parameter into the configuration database, as though it had always existed. For example, type this on the command line:

config set LogInterval 30

You can use config show LogInterval to show that it was set as intended. You can now edit the 25templatedemo file and replace the hardcoded number 20 with the LogInterval parameter. The resulting file will read:

# Template demo crontab entry:
 */{ $LogInterval } * * * * root /usr/bin/logger -t "Demo4" "... {
   use esmith::AccountsDB;
 
   $adb = esmith::AccountsDB->open_ro;
   $adb->get_prop('admin', 'ForwardAddress');
 
 } ..."

Now you can change the logger interval at any time by typing the following (replace the number 20 with whatever logger interval you want):

config set LogInterval 20
expand-template /etc/crontab

This ability to spontaneously introduce new configuration parameters is very important in the SME Server architecture. The configuration database is a high-level specification of how the overall system is supposed to behave for the end user. Configuration settings are like knobs on a stereo system. The templates, events, and actions, are the underlying machinery to carry out the user's wishes. When adding a new application to the system, it is important to be able to add new knobs on demand.

Now let us say that you want to introduce a parameter to enable or disable this logging function. At this point, you might start thinking of this logging activity as a service that you should be able to enable or disable. In this case the convention is to create a single service entry in the configuration database to manage both parameters.

To implement this, first delete the LogInterval parameter, which will no longer be needed:

db configuration delete LogInterval

Now create a service entry:

db configuration set loggerdemo service status enabled Interval 20

If you examine the configuration database you will see the new entry looks like this:

[root@gsxdev1 ~]# config show loggerdemo
loggerdemo=service
    Interval=20
    status=enabled
Important.png Note:
By convention, database keys are lower case, and property names are mixed case (e.g. Interval). The status property is an exception here, and is stored lower case.

Now edit the 25templatedemo file to look like this:

# Template demo crontab entry
 {
     my $status = $loggerdemo{status} || "disabled";
 
     return "# loggerdemo service is disabled."
         unless ($status eq "enabled");
 
     use esmith::AccountsDB;
 
     $adb = esmith::AccountsDB->open_ro;
     my $admin_email = $adb->get_prop('admin', 'ForwardAddress')
                         || 'admin';
 
     my $interval = $loggerdemo{Interval} || 10;
 
     $OUT = "*/$interval * * * * root /usr/bin/logger";
     $OUT .= " -t \"Demo4\" \"... $admin_email ...\"\n";
 }

Important.png Note:
The variable $OUT is a special variable used for output from a template. It is documented in the Text::Template documentation. For now, just think about it as the return value from the template, so set it to the value you want to print from this fragment. Note also that a template fragment can return static text without setting $OUT directly, as shown on the return line above.

This is more complicated than the original template, but it is also more flexible. Note that the initial comment (# Template demo crontab entry) is hardcoded, but the line that follows is generated from the configuration database parameters. The code in the template retrieves the loggerdemo service entry, retrieves the required properties, and returns the appropriate output. To experiment, try different combinations of parameters:

db configuration setprop loggerdemo Interval 10
expand-template /etc/crontab

and

config setprop loggerdemo status disabled
expand-template /etc/crontab

and so on.


Exercise 5: Adding a user interface screen

Let us add a nice user interface screen to adjust the logger interval. Create a new file called /etc/e-smith/web/functions/loggerdemo, with the following contents:

#!/usr/bin/perl -wT
 # vim: ft=xml:
 
 #----------------------------------------------------------------------
 # heading     : Demo
 # description : Logger
 # navigation  : 1000 1000
 #----------------------------------------------------------------------
 
 use strict;
 use warnings;
 
 use esmith::FormMagick::Panel::loggerdemo;
 
 my $f = esmith::FormMagick::Panel::loggerdemo->new();
 $f->display();
 
 __DATA__
 <form
     title="FORM_TITLE"
     header="/etc/e-smith/web/common/head.tmpl"
     footer="/etc/e-smith/web/common/foot.tmpl">
 
     <page name="First" pre-event="print_status_message()"
         post-event="change_settings">
 
         <field
             type="select"
             id="loggerdemo_Interval"
             options="10,20,30,40,50"
             value="get_interval()">
             <label>LABEL_LOGGERDEMO_INTERVAL</label>
         </field>
 
         <field
             type="select"
             id="loggerdemo_status"
             options="'disabled' => 'DISABLED', 'enabled' => 'ENABLED'"
             value="get_status()">
             <label>LABEL_LOGGERDEMO_STATUS</label>
         </field>
 
 
         <subroutine src="print_button('SAVE')" />
     </page>
 </form>

The file above describes the panel layout, which is written in FormMagick XML. Further details about FormMagick can be fiound in the Section called An overview of FormMagick in Chapter 10.

Another file provides the implementation, which goes into /usr/lib/perl5/site_perl/esmith/FormMagick/Panel/loggerdemo.pm:

#!/usr/bin/perl -w
 package esmith::FormMagick::Panel::loggerdemo;
 
 use strict;
 use warnings;
 use esmith::ConfigDB;
 use esmith::FormMagick;
 
 our @ISA = qw(esmith::FormMagick Exporter);
 
 our @EXPORT = qw();
 
 our $VERSION = sprintf '%d.%03d', q$Revision: 1.1 $ =~ /: (\d+).(\d+)/;
 
 our $db = esmith::ConfigDB->open or die "Couldn't open ConfigDB\n";
 
 sub get_status
 {
     return $db->get_prop("loggerdemo", "status");
 }
 
 sub get_interval
 {
     return $db->get_prop("loggerdemo", "Interval");
 }
 
 sub change_settings
 {
     my $fm = shift;
     my $q = $fm->{'cgi'};
 
     $db->set_prop('loggerdemo', 'status', $q->param("loggerdemo_status"));
     $db->set_prop('loggerdemo', 'Interval', $q->param("loggerdemo_Interval"));
 
     unless ( system ("/sbin/e-smith/expand-template", "/etc/crontab") == 0 )
     {
         $fm->error('ERROR_UPDATING');
         return undef;
     }
 
     $fm->success('SUCCESS');
 }
 
 1;

Important.png Note:
This code example calls expand-template. This is used for illustrative purposes only. All templates should be expanded by signalling events.

Similarly to events and actions, the /etc/e-smith/web/functions/ directory is a repository of potentially available functions. To make the new function actually show up in the user interface, create a symbolic link to it from the web manager cgi-bin directory, as follows:

    cd /etc/e-smith/web/panels/manager/cgi-bin
    ln -s ../../../functions/loggerdemo loggerdemo

Now, make sure the permissions and ownership are correct so that the web server can run your new function:

cd /etc/e-smith/web/functions
chown root:admin loggerdemo
chmod 4750 loggerdemo

We also need to run a program to rebuild the navigation bar. This is only required when adding or removing functions from the manager, and is normally handled automatically. Let's do it manually for now:

/etc/e-smith/events/actions/navigation-conf

Try the server manager now, reloading the navigation bar in your browser, if necessary. You will see a new category called Demo and a new function within it called Logger. It will look a bit bare with some upper-case words which signify phrases which have not been localised. We'll fix that in a moment.

Try experimenting with it - it is a nice little user interface for playing with the logger customization. Every time you change the settings, notice that the /etc/crontab file is updated appropriately.


Adding localizations

The SME Server is designed to support localization into any language. This is done by small files which describe the mapping from the upper case tags (seen above) to the appropriate words for the local language. Enter the following into /etc/e-smith/locale/en-us/etc/e-smith/web/functions/loggerdemo


<lexicon lang="en-us">

    <entry>
        <base>FORM_TITLE</base>
        <trans>Logger demo</trans>
    </entry>

    <entry>
            <base>Demo</base>
            <trans>Demo</trans>
    </entry>

    <entry>
            <base>Logger</base>
            <trans>Logger</trans>
    </entry>

    <entry>
            <base>LABEL_LOGGERDEMO_STATUS</base>
            <trans>Status</trans>
    </entry>

    <entry>
            <base>ENABLED</base>
            <trans>Enabled</trans>
    </entry>

    <entry>
            <base>DISABLED</base>
            <trans>Disabled</trans>
    </entry>

    <entry>
            <base>SAVE</base>
            <trans>Save</trans>
    </entry>

</lexicon>

Now, re-select the Logger function and the tags should now be replaced by English phrases. We can very easily add translations for other languages by adding new locale files in the same hierarchy.

Important.png Note:
The LABEL_LOGGERDEMO_INTERVAL tags has intentionally been left untranslated. Why don't you fix it now?


Exercise 6: Adding a new event type

Let us continue building on this example. Let us say that you want to add a hook to the logger demo, enabling other third party applications to receive a notification whenever the logger settings are changed. We need a new event type for this. Let us create a new event called loggerdemo-update:

mkdir -p /etc/e-smith/events/loggerdemo-update

The idea is that we will arrange for our user interface function to signal this new event whenever the settings are changed, instead of directly expanding the /etc/crontab file. Edit the /usr/lib/perl5/site_perl/esmith/FormMagick/Panels/loggerdemo.pm file and replace the line:

unless (system ("/sbin/e-smith/expand-template", "/etc/crontab") == 0)

with the line:

unless (system ("/sbin/e-smith/signal-event", "loggerdemo-update") == 0)

Now the loggerdemo user interface signals the new event whenever it saves the new settings to the configuration database. Next, we have to make sure that the event does what we need it to do - rebuild the /etc/crontab file.

cd /etc/e-smith/events/loggerdemo-update
mkdir -p templates2expand/etc
touch templates2expand/etc/crontab

Now the example should work just as before. You can edit the loggerdemo settings in the web manager, and see that the /etc/crontab file changes. But now other applications can also receive notifications of the loggerdemo-update event, by creating symbolic links from the /etc/e-smith/events/loggerdemo-update directory.

Important.png Note:
Panel implementation code should always signal events, and should never expand templates or modify files directly. These modifications should only be peformed in events.


Exercise 7: Thought experiment - adding a new server application

You have now learned most of the machinery required for integrating a new server application into the SME Server. Consider a hypothetical chat server, with a configuration file called /etc/chatserv.conf.

You would first decide if the chat server needs any new settings. If so, you can create a new service entry in the configuration database to hold those settings.

You would then create a templated version of /etc/chatserv.conf, by creating the directory /etc/e-smith/templates/etc/chatserv.conf with at least one template fragment. The template should generate the correct version of /etc/chatserv.conf based on the configuration database settings. Experiment with different combinations of settings and manually running expand-template /etc/chatserv.conf.

Then create a web manager function (if one is required) enabling users to edit the settings. An event, for example chatserv-update should be signalled whenever the settings are changed. You would normally link into an existing event, but this is a thought experiment.

Then create the file /etc/e-smith/events/chatserv-update/templates2expand/etc/chatserv.conf to ensure that the configuration file is updated. You may also need to restart the chatserv program after changing its configuration, so you'll need a /etc/e-smith/events/chatserver-update/services2adjust/chatserv link which contains the word restart.

Finally, figure out when you'll need these scripts to be run. At a minimum, you'll want to run them whenever the event is signalled indicating that the user has saved new settings from the web manager. A symbolic link should be created, so that the new event triggers your new action scripts. Let's say you also need to reconfigure and restart your server each time a user is added. In that case, you would also create symlinks from the user-create event to your action scripts.

We are done - the new server has been integrated into the system. Note that every single one of these steps involved creating new files and directories, and reading/writing from the configuration database. No existing files were edited! You can easily imagine packaging these new files and directories into an RPM.


Customization guidelines

When creating applications:

  • You can create new configuration parameters.
  • You can add new configuration templates under /etc/e-smith/templates/.
  • You can add fragments to any of the existing templates under /etc/e-smith/templates/.
  • You can arrange for actions to be triggered upon package pre-install, post-install, pre-uninstall, or post-uninstall.
  • You can link new actions into the events listed in Chapter 7.
  • You can create new events to allow your feature set to be expanded. Do not create events for any other reason. They are not a replacement for function calls.
  • You can arrange for new server programs to be started up at boot time.
  • Typically you would expand a template at application post-install time, application post-uninstall time, and one or more of the other events.
  • You can add new web functions to the navigation bar. Remember, panels should only retrieve and modify database values, perform validation, and signal events.

That is all! Applications should not make any extensions to the system other than these. For example, an application should not:

  • Change the kernel or add new kernel modules.
  • Edit configuration files directly - templates must be used.
  • Link actions into any events other than the ones listed above or new events that you create. All built-in events other than the ones listed above are subject to change without notice in new SME Server releases.
  • Directly access the per-user email store (i.e. the Maildir and related subdirectories within each user's home directory). This access should be performed via the IMAP server as the location and format of a user's home directory may change between releases.
  • Take over the function of existing servers (i.e. shut down qmail and Apache and take over ports 25 and 80). The SME Server has features for proxying email and web requests to other servers on the system.

Do not expand templates at boot time. The only thing that should be happening during a normal system startup is to start servers. Templates should be expanded when it is necessary to change the system configuration (i.e. when a setting is changed, when the IP address changes, etc.) A normal shutdown or reboot should not trigger configuration changes. The bootstrap-console-save event will be run after a system reconfiguration, but will not run if the system does not require reconfiguration.

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:

  1. summary and description of the software
  2. package name
  3. version number
  4. copyright information
  5. category/group to which the software belongs
  6. name and email address of the packager
  7. pre-requisites to installing this package
  8. ... and more

Selecting and creating RPMs for your application

Your application will typically depend on several components:

  1. 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.
  2. 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.
  3. 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.
  4. 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

Time.png Outdated:
The information on this page maybe no longer relevant.

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
Important.png Note:
Shell/login access is disabled by default to enhance the security of the SME Server. Shell access should only be provided to users who require it and who can be trusted to maintain system security.

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

Time.png Outdated:
The information on this page maybe no longer relevant.

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.

Important.png Note:
RPMs need to be installed as root, but you should not log in as the root user. Instead, you should create a normal user and provide them with 'root' privileges via the sudo command. To provide full sudo rights to the user joe, use the su - -c /usr/sbin/visudo and add the following line to the end of the file: joe ALL=(ALL) ALLYou can then use the sudo to run commands as root when required. You will be prompted for your password (not the root password) which ensures that someone else isn't using your terminal.

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.

The SME Server development environment

Configuring your development environment

The SME Server source code is checked into CVS at koozali.org. SME Server code is stored in the CVS on shell.koozali.org in two repositories:

Reminder: The SME Server source code is released under the GPL. You must release the source code to all modifications. If you make improvements, please raise a bug and attach a patch so the change can be discussed and pulled back into the base for everyone to share.

Only developers who are going to put patches back into CVS and build new packages need shell.koozali.org CVS access. The sources are freely available and patches are gratefully received. Just follow the instructions in this section and attach the patch(es) to the Bugzilla entry, explaining why the change should be made.


Local environment

  • Install cvs
yum install cvs rsh
  • Setup CVS to use ssh by creating /etc/profile.d/smebuild.sh with the following content
# Developer environment
# This gets symlinked into /etc/profile.d 

export CVS_RSH=ssh # tell CVS to use ssh

# DO NOT set CVSROOT

alias rm='rm -i'
alias cp='cp -i --preserve=timestamps'
alias mv='mv -i'

You have to logout and login again to the console for changes to take effects.

Access to build system

Check updates/status on the build server: http://buildsys.contribs.org


Warning.png Warning:
For contrib builders using the plague-client-0.5.0 you will need to downgrade to correct version, to fix this please do:
rpm -e plague-client plague-common
yum --enablerepo=smecontribs install plague-client

Ask admin@contribs.org for certificates, give the email address to use for notifications.

yum --enablerepo=smecontribs install plague-client

From now, do not use account "root" anymore. Use a dedicated dev account. Copy certificates and config file to ~/

.plague-client.cfg
.username.pem
.contribs-upload-ca.pem
.contribs-server-ca.pem

Don't forget to set the proper privileges on the file

chmod 600 .username.pem

Check it's working

plague-client list_builders

Builders:
------------------------------------------------------------------------------------------
 build64-1.contribs.org      x86_64 amd64 ia32e noarch i386 i486 i586 i686 athlon available
 build32-1.contribs.org      i386 i486 i586 i686 athlon noarch  available

CVS shell.koozali.org access

  • SME Server code is stored in the CVS on http://shell.koozali.org. To be able to work on your code in the SME Server CVS repository you need an account on Koozali.org. With this account the development team can give you access to the CVS repository.
  • After your Koozali.org account has been created you can ask the development team to give you developer access to smecontribs. Create a bug in the Bug Tracker as usual.
  • If local username is different to shell.koozali.org username edit ~/.ssh/config:
Host  shell.koozali.org
User  koozaliusername (without @shell.koozali.org)
port 222
  • Don't forget to set the proper privileges on the file
chmod 600 ~/.ssh/config

Import source to shell.koozali.org

Email admin@contribs.org with the location of your rpm, it will be imported into the build system for you. Follow the same procedure when an upstream release occurs, eg a new .tar.gz, Update your local cvs with:

cvs update -dPA

If you have developer access to buildsys you can follow this guide

https://wiki.contribs.org/Package_Import

Import cvs in your workspace

You can use ~/home/smeserver or whatever suits.

mkdir ~/home/smeserver
cd ~/home/smeserver
cvs -z3 -d:ext:shell.koozali.org:/cvs/smeserver co -P rpms 
mkdir ~/home/smecontribs
cd ~/home/smecontribs 
cvs -z3 -d:ext:shell.koozali.org:/cvs/smecontribs co -P rpms 

To refresh run the following from the rpms directory, or any lower directory with a CVS dir

cvs update -dPA

Modifying a SME Server package

Raise a Bugzilla entry

Before you make any changes to a package, you need to have a Bugzilla entry which specifies the problem and preferably proposes a fix. Raising the bug before you do the work allows others to comment on the proposed approach and can save significant time when you go to submit the changes. The change should also be approved by the Development Manager if it is meant for near-term release. You will need the Bugzilla bugid when you check in the changes.

All changes must have an associated Bugzilla entry. The bug tracker is here: http://bugs.contribs.org/

If a relevant bug does not exist, raise one. If the bug exists, assign it to yourself to show that you are working on it:

For this exercise, let's look at bug 1174 "yum-import-keys should not import duplicates" http://bugs.contribs.org/show_bug.cgi?id=1174.


Modify the package

If you are modifying an existing file, the simplest way to determine the package is to install the relevant version and run rpm -qf on the file to be modified:

[gordonr@smebuild actions]$ rpm -qf /etc/e-smith/events/actions/yum-import-keys
smeserver-yum-1.1.2-05

and so, we want to modify the smeserver-yum package.

All packages on the SME Server ISO/CD must be checked into shell.koozali.org CVS. The only exceptions are packages which come from the following upstream repositories: CentOS and dag.


You can now retrieve one of the packages from shell.koozali.org. In this case, we want to modify the smeserver-yum package, so let's retrieve it from shell.koozali.org:

cvs -z3 -d:ext:shell.koozali.org:/cvs/smeserver co -P smeserver-yum

Change to work directory

cd smeserver/rpms/smeserver-yum/sme7

To prepare a tree

cvs update -dPA   
make clean        
make prep
Make a patch

Then switch to the tree and make modification.

Method A

In the prepared dir copy a file you want to modify like so:

cp yum-import-keys yum-import-keys.{patchname}

Then modify the original file yum-import-keys. To add new files touch yum-import-keys.{patchname} so it is empty.

Once you have all the files you want patched copied and changed then you can build the patch (from the sme7 dir) with:

make patch SUFFIX={patchname}

It will build and add the patch for you. It should be named "name-version-{patchname}.patch". It will also add the patch to CVS.

Method B

Make a copy of the prepared directory, edit directly, then make a patch

cp -R smeserver-yum-2.0.0 smeserver-foo-2.0.0.old
diff -urN smeserver-foo-2.0.0.old smeserver-yum-2.0.0 > smeserver-yum-2.0.0-importKeys.patch
Apply a patch

For example, check if a translation patch is available:

If patch size is 0 bytes there is nothing to do

Else go to the package folder in your tree and do :

wget http://translate.contribs.org/patches/contribs/{name}-locale-{date}.patch
cvs add {name}-locale-{date}.patch

Then you need to follow instructions in next part....

You may add yourself some translations , and wait for patch to be created (at about 2 AM GMT-6, or 6 PM Sydney)

Edit the spec
nano -w smeserver-foo.spec
#increase the release
%define release 15

#add the patch
Patch2: smeserver-foo-1.2-widget.patch

#update the changelog, include the bug number
* Fri Jan 11 2008 John Smith <smith@foo.net> 1.2-15
- fixed foo to create bar [SME 3470]

#apply the patch in %setup
%patch2 -p1
Commit

Build the rpm locally to test, (note, this deletes the working tree!)

make local       

Once you are satisfied and want to submit the package to the build server commit your changes. (Please use descriptive comments so that other developers are aware of what is happening. Comments will appear on the subject line of the commit email that get send to the other developers.)

cvs commit -m 'your descriptive commit message here'

You can automate the addition of the comments in the spec file with the command 'clog'.

Important.png Note:
You will need to be in the sme7, sme8, or contribs7 directory for this to work

rm -f clog
cvs commit -m "$(make clog)"

CVS cheat sheet Package_Modification/More cvs commands

Build

Tag all files as belonging to a particular build version

make tag

Submit the request to the build server which will checkout the recently tagged version and build it

make build

Always do "make tag" before "make build"

Always ensure you are working with the latest version (cvs update -dPA)

You and updatesteam will get an email on successful build. Only you will get an email on failed build.

You can check the build system is working:

plague-client list uid  {task number}

Releasing a contrib package

After the make build command the build system will try and build your package. After a successful build it will be put in the smetest repository. You should be notified of the result of the build by e-mail.

Once a package is build successfully you should verify your changes, ideally you would have a bug to verify for each modification. After verification of all relevant changes and bugs you can release the package like this:

  1. Login to shell.contribs.org like this: ssh -p222 username@shell.koozali.org
  2. Navigate to the smeserver directory: cd /teams/smeserver
  3. The teams directory contains a few directories of which two are relevant, the first is called updates which will hold the SME Server packages, the other is called contribs and will hold build contribs. Suppose we would like to release our contrib we would proceed like this: cd contribs/9
  4. Now copy the relevant package from smetest to smecontribs, old versions are removed automatically cp smetest/package-name-version.rpm smecontribs/


Important.png Note:
Within a period of one hour the package should be moved to the smecontribs repository and be available as soon as the mirrors synchronize.

Once the server successfully builds it will automatically be pulled on the next repo update run (40 past the even hours MDT). The package will either be put into the smedev (new package) or smetest (exist in higher repo) After verification the package is manually moved from smedev/smetest to smecontribs (for contribs) or smeupdates-testing (for packages in base)

Mailing Lists

Subscribe yourself to the devinfo mailinglist. This is the place to discuss the development of the server and contribs. If you have other questions, not regarding development please use the forums.