Difference between revisions of "SME Server:Documentation:Developers Manual:Chapter12"

From SME Server
Jump to navigationJump to search
m (Adjusting header(s))
m (→‎Exercise 2: The magic of templates: add note about esmith perl libraries location for SME9)
 
(4 intermediate revisions by 2 users not shown)
Line 93: Line 93:
  
 
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.
 
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.
 +
{{Note box|msg=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.
  
<div class="NOTE"><blockquote class="NOTE">
+
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.}}
 
 
'''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, <span class="emphasis">''whitespace outside braces is significant''</span> and will be copied literally to the output file.
 
 
 
</blockquote></div>
 
  
 
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 <tt class="FILENAME">/var/log/messages</tt> log file every 20 minutes will contain the actual current administrative email address, rather than a hardcoded text message.
 
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 <tt class="FILENAME">/var/log/messages</tt> log file every 20 minutes will contain the actual current administrative email address, rather than a hardcoded text message.
Line 109: Line 104:
  
 
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.
 
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.
 +
 +
{{Note box|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 <span class="emphasis">''E-mail''</span> function can be found in the file <tt class="FILENAME">/usr/lib/perl5/site_perl/esmith/FormMagick/Panel/emailsettings.pm</tt>. If you study the part of this script that gets executed when the user clicks <span class="emphasis">''Save''</span>, you will find some Perl code that saves the administrator e-mail address into the configuration database and signals the <tt class="FILENAME">email-update</tt> event - thus informing the system that the email settings have changed:
 
To understand this example in detail, let's start from the top by studying the user interface. The implementation section of the <span class="emphasis">''E-mail''</span> function can be found in the file <tt class="FILENAME">/usr/lib/perl5/site_perl/esmith/FormMagick/Panel/emailsettings.pm</tt>. If you study the part of this script that gets executed when the user clicks <span class="emphasis">''Save''</span>, you will find some Perl code that saves the administrator e-mail address into the configuration database and signals the <tt class="FILENAME">email-update</tt> event - thus informing the system that the email settings have changed:
Line 316: Line 313:
 
     Interval=20
 
     Interval=20
 
     status=enabled
 
     status=enabled
 
+
{{Note box|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.}}
<div class="NOTE"><blockquote class="NOTE">
 
 
 
'''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.
 
 
 
</blockquote></div>
 
  
 
Now edit the <tt class="FILENAME">25templatedemo</tt> file to look like this:
 
Now edit the <tt class="FILENAME">25templatedemo</tt> file to look like this:
Line 344: Line 336:
 
  }
 
  }
 
</nowiki>
 
</nowiki>
 
+
{{Note box|msg=The variable <var class="LITERAL">$OUT</var> is a special variable used for output from a template. It is documented in the <code class="CLASSNAME">Text::Template</code> 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 <var class="LITERAL">$OUT</var> directly, as shown on the <var class="LITERAL">return</var> line above.}}
<div class="NOTE"><blockquote class="NOTE">
 
 
 
'''Note: '''The variable <var class="LITERAL">$OUT</var> is a special variable used for output from a template. It is documented in the <code class="CLASSNAME">Text::Template</code> 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 <var class="LITERAL">$OUT</var> directly, as shown on the <var class="LITERAL">return</var> line above.
 
 
 
</blockquote></div>
 
  
 
This is more complicated than the original template, but it is also more flexible. Note that the initial comment (<var class="LITERAL"><nowiki># Template demo crontab entry</nowiki></var>) is hardcoded, but the line that follows is generated from the configuration database parameters. The code in the template retrieves the <span class="emphasis">''loggerdemo''</span> service entry, retrieves the required properties, and returns the appropriate output. To experiment, try different combinations of parameters:
 
This is more complicated than the original template, but it is also more flexible. Note that the initial comment (<var class="LITERAL"><nowiki># Template demo crontab entry</nowiki></var>) is hardcoded, but the line that follows is generated from the configuration database parameters. The code in the template retrieves the <span class="emphasis">''loggerdemo''</span> service entry, retrieves the required properties, and returns the appropriate output. To experiment, try different combinations of parameters:
Line 467: Line 454:
 
  1;
 
  1;
 
</nowiki>
 
</nowiki>
 
+
{{Note box|msg=This code example calls expand-template. This is used for illustrative purposes only. All templates should be expanded by signalling events.}}
<div class="NOTE"><blockquote class="NOTE">
 
 
 
'''Note: '''This code example calls expand-template. This is used for illustrative purposes only. All templates should be expanded by signalling events.
 
 
 
</blockquote></div>
 
  
 
Similarly to events and actions, the <tt class="FILENAME">/etc/e-smith/web/functions/</tt> 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:
 
Similarly to events and actions, the <tt class="FILENAME">/etc/e-smith/web/functions/</tt> 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:
Line 501: Line 483:
  
 
   
 
   
<lexicon lang="en-us">
+
<lexicon lang="en-us">
    <!-- vim: ft=xml:
+
    -->
 
 
     <entry>
 
     <entry>
 
         <base>FORM_TITLE</base>
 
         <base>FORM_TITLE</base>
Line 543: Line 524:
  
 
Now, re-select the <span class="emphasis">''Logger''</span> 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.
 
Now, re-select the <span class="emphasis">''Logger''</span> 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.
 
+
{{Note box|msg=The <var class="LITERAL">LABEL_LOGGERDEMO_INTERVAL</var> tags has intentionally been left untranslated. Why don't you fix it now?}}
<div class="NOTE"><blockquote class="NOTE">
+
</div></div><div class="SECT1">
 
 
'''Note: '''The <var class="LITERAL">LABEL_LOGGERDEMO_INTERVAL</var> tags has intentionally been left untranslated. Why don't you fix it now?
 
 
 
</blockquote></div></div></div><div class="SECT1">
 
 
----
 
----
  
Line 572: Line 549:
  
 
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 <tt class="FILENAME">/etc/e-smith/events/loggerdemo-update</tt> directory.
 
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 <tt class="FILENAME">/etc/e-smith/events/loggerdemo-update</tt> directory.
 
+
{{Note box|msg=Panel implementation code should <span class="emphasis">''always''</span> signal events, and should <span class="emphasis">''never''</span> expand templates or modify files directly. These modifications should only be peformed in events.}}
<div class="NOTE"><blockquote class="NOTE">
+
</div><div class="SECT1">
 
 
'''Note: '''Panel implementation code should <span class="emphasis">''always''</span> signal events, and should <span class="emphasis">''never''</span> expand templates or modify files directly. These modifications should only be peformed in events.
 
 
 
</blockquote></div></div><div class="SECT1">
 
 
----
 
----
  
Line 607: Line 580:
 
* You can add fragments to any of the existing templates under <tt class="FILENAME">/etc/e-smith/templates/</tt>.
 
* You can add fragments to any of the existing templates under <tt class="FILENAME">/etc/e-smith/templates/</tt>.
 
* You can arrange for actions to be triggered upon package pre-install, post-install, pre-uninstall, or post-uninstall.
 
* 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 [http://wiki.contribs.org/The_SME_Server_Developer%27s_Guide#STANDARD-EVENTS the Section called ''Standard events and their arguments'' in Chapter 7].
+
* You can link new actions into the events listed in [[SME_Server:Documentation:Developers_Manual:Chapter7#Standard_events_and_their_arguments| Chapter 7]].
 
* You can create new events to allow your feature set to be expanded. Do <span class="emphasis">''not''</span> create events for any other reason. They are not a replacement for function calls.
 
* You can create new events to allow your feature set to be expanded. Do <span class="emphasis">''not''</span> 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.
 
* You can arrange for new server programs to be started up at boot time.

Latest revision as of 10:57, 7 June 2015

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.