The CerfCube as an Embedded Linux Server

by Ricardo Restrepo

The CerfCube is an embedded system manufactured by Intrinsyc. It is based on an ARM processor, which is the processor used in many PDAs. In this article I first explain how to turn a CerfCube into an FTP and HTTP server. In order to have a complete web server with the ability to run CGIs, we compile Perl and install all the necessary modules for CGI development under Perl. Then I explain how to update the OpenSSH tools in the CerfCube. This implies adding libraries to the cross compilation toolchain, patching source files and configuring the CerfCube.

Prerequisites

Before beginning this project, one should have:

  1. a complete development system with the usual development and network tools (GCC, make, Perl, ssh, scp and so on). Most standard Linux distributions provide all the necessary development tools.

  2. a working cross compiling toolchain built around glibc version 2.2.3 or newer. Although other C libraries generate smaller binaries, the applications we intend to build work without problems under glibc. They may not run at all under other C libraries.

  3. a target ARM embedded system with a working network connection to the development system and ssh tools. Although the instructions given here are specific to the CerfCube, they are valid for other systems as well.

Disclaimer

The steps shown here have been thoroughly tested and are known to work. I am not responsible for damage caused by misuse of the directions presented here. We do things as root in this exercise, which requires special attention.

Building the Applications

Before building the applications and libraries, it is a good idea to create one directory for storing the downloaded files and another for storing the files we will upload to the CerfCube. Don't forget to have your arm-linux tools available in your PATH:

devsys# export BINS_DIR=~/develop/binaries
devsys# export SRC_DIR=~/develop/sources
devsys# mkdir -p $BINS_DIR
devsys# mkdir -p $SRC_DIR

Boa is a small HTTP server that also provides a CGI environment. We are using boa-0.94.13, which is easy to compile and offers good performance.

devsys# cd $SRC_DIR
devsys# wget http://www.boa.org/boa-0.94.13.tar.gz
devsys# tar zxf boa-0.94.13.tar.gz
devsys# cd boa-0.94.13/src

You may change your SERVER_ROOT (the parent directory of your server's file tree) by editing the variable in defines.h. The server root also can be set at the command line when invoking Boa or in the configuration file boa.conf. We use this last option:

devsys# ac_cv_func_setvbuf_reversed=no ./configure --sysconfdir=/etc
devsys# make CC=arm-linux-gcc
devsys# cp boa ../boa.conf $BINS_DIR

The assignment ac_cv_func_setvbuf_reversed=no precaches a configuration value; without it, configure would try to find the value by compiling and running a small program. This method fails because the program is generated by the arm-linux-gcc compiler for an ARM processor and it is run on the development system under a different processor. The arguments to the function setvbuf are not reversed in glibc, so we precache the value "no". We want the configuration file to reside in /etc in the target system, so we set this parameter for sysconfdir.

In the CerfCube, create directories for the web server:

CerfCube# mkdir -p /etc/boa

We use proftpd-1.2.7 as our FTP server. Proftpd is stable, safe and already the server of choice for most standard distributions.

devsys# cd $SRC_DIR
devsys# wget ftp://ftp.proftpd.org/distrib/source/proftpd-1.2.7.tar.gz
devsys# tar zxf proftpd-1.2.7.tar.gz
devsys# cd proftpd-1.2.7
devsys# ac_cv_func_setpgrp_void=yes ./configure --sysconfdir=/etc --host=arm-linux
devsys# make
devsys# cp proftpd $BINS_DIR

Again, we precache a configuration value to avoid running a test that would fail. In glibc, setpgrp is declared as a void function.

We need to create a configuration file for the FTP server (proftpd.conf) file:

devsys# cd $BINS_DIR
devsys# cat > proftpd.conf << --EOF--                                                                 
ServerName                      "CerfCube FTP server"     
ServerType                      inetd                                    
DefaultServer                   on                                       
ScoreboardFile                  /var/run/proftpd                         
Port                            21                                       
                                                                     
Umask                           022                                      
                                                                         
MaxInstances                    30                                    
                                                                      
User                            nobody                             
Group                           nogroup                            
                                                                         
# Normally, we want files to be overwriteable.                           
<Directory />                                                            
  AllowOverwrite                on                                       
</Directory>                                                             
                                                                         
# A basic anonymous configuration, no upload directories.                
<Anonymous ~ftp>                                                         
  User                          ftp                                      
  Group                         nogroup                                  
                                                                         
  # We want clients to be able to login with "anonymous" as well as "ftp"
  UserAlias                     anonymous ftp                            
                                                                         
  # Limit the maximum number of anonymous logins                         
  MaxClients                    10                                       
                                                                         
  # We want 'welcome.msg' displayed at login, and '.message' displayed   
  # in each newly chdired directory.                                     
  DisplayLogin                  welcome.msg                              
  DisplayFirstChdir             .message                                 
                                                                         
  # Limit WRITE everywhere in the anonymous chroot                       
  <Limit WRITE>                                                          
    DenyAll                                                              
  </Limit>                                                               
                                                                         
</Anonymous>                                                             
--EOF--

Now, we need to build inetd to run proftpd. Inetd is a dæmon that checks incoming connections to ports and launches the application that handles the request to the specific port, as configured in inetd.conf. Inetd is part of the inetutils package, which also contains many other Internet utilities. We use inetutils-1.4.2.

devsys# cd $SRC_DIR
devsys# wget ftp://ftp.gnu.org/pub/gnu/inetutils/inetutils-1.4.2.tar.gz
devsys# tar zxf inetutils-1.4.2.tar.gz
devsys# cd inetutils-1.4.2
devsys# ac_cv_func_setvbuf_reversed=no ./configure --disable-clients \
> --disable-libls --disable-servers --enable-inetd 
> --disable-dependency-tracking --sysconfdir=/etc --host=arm-linux
devsys# make
devsys# cp inetd/inetd $BINS_DIR

As we are interested only in inetd, all other clients and servers offered by the package should be disabled.

Building the Secure Shell Utilities

To build openssh-3.5p1, we need the presence of the zlib and OpenSSL libraries in the cross compilation toolchain.

  1. Building zlib-1.1.4

    The zlib library contains routines for data compression and decompression. It is widely used by other applications.

    devsys# cd $SRC_DIR
    devsys# wget http://unc.dl.sourceforge.net/sourceforge/libpng/zlib-1.1.4.tar.gz
    devsys# tar zxf zlib-1.1.4.tar.gz
    devsys# cd zlib-1.1.4
    

    Before configuring, be sure the prefix points to the proper place in your development system. It must be a directory where your arm-linux-gcc looks for a lib directory containing the libraries. The command arm-linux-gcc -print-search-dirs gives you an idea. The last library listed in my development system, /usr/local/arm/2.95.3/arm-linux/lib, indicates the prefix should be /usr/local/arm/2.95.3/arm-linux.

    devsys# ./configure --shared --prefix=/usr/local/arm/2.95.3/arm-linux
    

    Edit the Makefile to make lines 15 and 24 read GCC instead of arm-linux-gcc.

    devsys# make && make install
    devsys# cp -pd libz.so* $BINS_DIR
    
  2. Building openssl-0.9.7a

    The OpenSSL library provides functions for data encryption.

    devsys# cd $SRC_DIR
    devsys# wget http://www.openssl.org/source/openssl-0.9.7a.tar.gz
    devsys# tar zxf openssl-0.9.7a.tar.gz
    devsys# cd openssl-0.9.7a
    

    Again, be careful to set prefix to the right value.

    devsys# ./config shared zlib-dynamic no-asm no-dso no-krb5 \
    > --prefix=/usr/local/arm/2.95.3/arm-linux \
    > --openssldir=/usr/local/arm/2.95.3/arm_linux/openssl
    

    Edit the Makefile to erase -m486 in line 64.

    devsys# make install CC=arm-linux-gcc
    devsys# cp -pd lib*so* $BINS_DIR
    

SSH Client, Server and Utilities

OpenSSH provides a set of secure utilities to establish encrypted connections. From all the utilities provided we use a secure shell client (ssh), a secure shell server (sshd), a secure copy utility (scp), a key generator (ssh-keygen) and configuration files for ssh (ssh-config) and for sshd (sshd_config). We use openssh-3.5p1.

devsys# cd $SRC_DIR
devsys# wget ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-3.5p1.tar.gz
devsys# tar zxf openssh-3.5p1.tar.gz

OpenSSH is not ready for cross compilation out of the box. The configure.ac file has to be modified to prevent some tests from running with binaries generated for ARM. A patch is provided, but the purpose of the patch is to give the macro AC_TRY_RUN a third option with the result known for the test. Read Listing 1 to see this idea in action.

Listing 1. openssh-3.5pl.diff

Another change is to use file checking directly (with the construct of test -f <file>) instead of using the AC_CHECK_FILE macro. File checking is used in two cases, for checking the existence of /dev/ptmx and for checking /dev/ptc. Be aware that those checks are made in the development system, not on the CerfCube. My development system has ptmx and doesn't have ptc, the same as my CerfCube.

Patch with the supplied file (openssh-3.5p1.diff) [see Listing 1] or change configure.ac by hand following the previous hints:

devsys# cd openssh-3.5p1
devsys# patch -Np0 -i ../openssh-3.5p1.diff

Now, run autoconf to generate the configure script. If autoconf complains it does not find configure.in, upgrade your version. (This is the case with Red Hat 7.2). Get the newer version from ftp.gnu.org/pub/gnu/autoconf/autoconf-2.57.tar.gz, then compile and install it.

devsys# autoconf
devsys# ./configure --host=arm-linux --target=arm-linux \
> --build=i686-linux --libexecdir=/bin --sysconfdir=/etc/ssh \
> --disable-lastlog --disable-utmp --disable-largefile \
> --disable-utmpx --disable-wtmp --disable-wtmpx --without-pam \
> --without-4in6 --with-pid-dir=/var/run --with-ipv4-default
devsys# make
devsys# cp ssh scp ssh-keygen sshd sshd_config ssh_config $BINS_DIR
Uploading the Applications

In the first step we strip all binaries of unneeded symbols:

devsys# cd $BINS_DIR
devsys# arm-linux-strip --strip-all *

Don't worry that some text formats will not be recognized; the binaries are stripped anyway.

Transferring Files from the Development System to the CerfCube

Having a password-less ssh (and scp) connection to the CerfCube is advantageous. Otherwise, the password is requested before every scp transfer. When we compile Perl, this could mean typing the same password more than 30 times.

To achieve a password-less ssh connection to the CerfCube, type devsys# cd ~/.ssh. Then, check to see if you have the file id_dsa.pub in this directory. If not, generate it with devsys# ssh-keygen -t dsa -P "". Accept the default directory offered by pressing Enter, and copy this key to your CerfCube:

CerfCube# mkdir -p /root/.ssh
CerfCube# cd /root/.ssh

Send your id_dsa.pub key from your development system. You should be asked for the root password in the CerfCube. I am assuming an address 192.168.2.7; change it to suit your configuration.

devsys# scp id_dsa.pub root@192.168.2.7:/root/.ssh

In the CerfCube, append the id_dsa.pub key to the authorized keys file:

CerfCube# cat id_dsa.pub >> authorized_keys2

Now, we can start transferring files with devsys# cd $BINS_DIR. We have to change the name of scp to transfer it, though. The running instance of scp does not let itself be replaced by another binary with the same name and complains about being busy. The same is valid for sshd.

devsys# mv scp scpnew
devsys# mv sshd sshdnew
devsys# scp libcrypto.so.0.9.7 libssl.so.0.9.7 \
> libz.so.1.1.4 root@192.168.2.7:/usr/lib
devsys# scp gdb boa ssh ssh-keygen scpnew root@192.168.2.7:/usr/bin
devsys# scp sshdnew inetd proftpd 
devsys# scp proftpd.conf root@192.168.2.7:/etc
devsys# scp ssh*_config root@192.168.2.7:/etc/ssh
devsys# scp boa.conf root@192.168.2.7:/etc/boa

We register the new libraries in the CerfCube with CerfCube# ldconfig -v. Observe that libz appears as modified; actually, it was upgraded to the latest version.

In the CerfCube we activate sshd and scp but leave the old versions just in case:

CerfCube# cd /usr/bin
CerfCube# mv scp scpold
CerfCube# mv scpnew scp
CerfCube# cd /usr/sbin
CerfCube# mv sshd sshdold
CerfCube# mv sshdnew sshd
Compiling Perl

Perl 5.8 has a feature that tests made at configure time are run remotely in the CerfBoard. This implies, of course, the necessity of working ssh tools on both the CerfCube and the development system. For added comfort, a password-less ssh connection, as set before, is advantageous.

devsys# export PFIX=/usr/local/arm/2.95.3/arm-linux
devsys# cd $SRC_DIR
devsys# wget http://www.cpan.org/src/5.0/stable.tar.gz
devsys# tar zxf stable.tar.gz
devsys# cd perl-5.8.0
devsys# ./Configure -des -Dusecrosscompile -Dtargethost=192.168.2.7 \
> -Dtargetdir=/tmp -Dtargetuser=root -Dtargetarch=arm-linux \
> -Dcc=arm-linux-gcc -Dusrinc=$PFIX/include -Dincpth=$PFIX/include \
> -Dlibpth=$PFIX/lib
devsys# make

Configuration is done with sensible default values, thanks to the -des options. Remember to put the arm-linux utilities in your search path. At the end of the compilation, you should end up with an executable named miniperl--not so mini, considering it is about 1MB after stripping. As with some other cross compilation aware applications, this one tries to run cross compiled binaries in the wrong environment, generating some error messages. But, Perl is compiled. Upload to your board and enjoy.

devsys# scp miniperl root@192.168.2.7:/usr/bin/perl
Postconfiguration of the CerfCube

To reflect the configuration of the FTP server, do the following on the CerfCube:

  1. Add the following line to /etc/inetd.conf:

    CerfCube# echo "ftp stream tcp nowait root /usr/sbin/proftpd proftpd"
    >> /etc/inetd.conf
    
  2. Add the user ftp and create the directory from which files will be served (/home/ftp):

    CerfCube# echo "ftp:*:100:100::/home/ftp:/bin/false" >> /etc/passwd
    CerfCube# mkdir /home/ftp
    
  3. Add the group nogroup, with the ID number 100, as set in the previous line:

    CerfCube# echo "nogroup:*:100:" >> /etc/group
    

Use the following steps to configure the ssh tools:

  1. Generate keys for the ssh utilities in the /etc/ssh directory (the sysconfdir at configure time):

    CerfCube# ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -P ""
    CerfCube# ssh-keygen -t rsa1 -f /etc/ssh/ssh_host_rsa_key -P ""
    CerfCube# ssh-keygen -t rsa -f /etc/ssh/ssh_host_key -P ""
    
  2. Create the sshd user:

    CerfCube# echo "sshd:*:101:100::/var/run/sshd:/bin/false" >> /etc/passwd
    
  3. Edit /etc/ssh/sshd_config to contain the line UsePrivilegeSeparation no and uncomment it. If you want to have a privilege separation directory, it must be /var/empty, which is erased every time you turn off the CerfCube. Be sure to have it created in the startup scripts before sshd is started. Otherwise, sshd won't start.

These are the steps necessary to configure the web server:

  1. Create the directory where the server should keep its documents. Remember, we previously created the directory /etc/boa, and the configuration file boa.conf is in that directory.

    CerfCube# mkdir -p /home/httpd/cgi-bin
    
  2. To run the web server, /etc/boa/boa.conf needs to be edited in at least the following directives:

    ErrorLog: set it to /dev/null to avoid error logging
    AccessLog: comment out to avoid access logging
    MimeTypes: set to /dev/null if you don't have a mime.types file
    DocumentRoot: set it to the directory from which you will be serving your html documents (/home/http)
    ScriptAlias: set the second part to the directory in which your CGIs will reside, (/home/httpd/cgi-bin
     in this case).
    
Configuring Perl for CGI

The CGI module is standard in the latest Perl distributions, but our Perl installation in the CerfCube does not include any libraries. The CGI module depends on other modules. Generating those modules in the CerfCube requires a working C compiler that, of course, does not fit in the CerfCube. The solution is to copy the necessary modules from the Perl installation on your development system. Perl 5.6 and 5.8 modules work fine; other Perl versions were not tested.

Using the configuration values given when compiling Perl, modules are expected to reside in a directory under /usr/local/lib/perl5. This could be the directory 5.8.0. Create that directory first:

CerfCube# mkdir -p /usr/local/lib/perl5/5.8.0/{CGI,warnings}

If your development system has Perl 5.6, simply issue the following command from there:

devsys# cd /usr/lib/perl5/5.6.0
devsys# scp CGI.pm overload.pm shellwords.pl root@192.168.2.7:/usr/local/lib/perl5/5.8.0

If your development system has Perl 5.8, you need some more files:

devsys# cd /usr/lib/perl5/5.8.0
devsys# scp CGI.pm overload.pm Carp.pm Exporter.pm strict.pm vars.pm \
> warnings.pm constant.pm overload.pm root@192.168.2.7:/usr/local/lib/perl5/5.8.0
devsys# scp CGI/Util.pm root@192.168.2.7:/usr/local/lib/perl5/5.8.0/CGI
devsys# scp warnings/register.pm root@192.168.2.7:/usr/local/lib/perl5/5.8.0/warnings
Testing the Servers

You can start your Boa HTTP server either manually or with a startup script. To test the server, create an index.html file in /home/httpd. Now, point a browser to http://192.168.2.7/index.html. You should see the page you created. If you type http://192.168.2.7/, the index.html file is interpreted as a directory listing, and the HTML contents are displayed as plain text in the browser.

To test your CGI environment, use the following script (found in stein.cshl.org/WWW/CGI, as an example to the Perl module CGI.pm). Name it test.cgi and place it in /home/httpd/cgi-bin.

#!/usr/bin/perl
use CGI qw(:standard);
print header;
print start_html('A Simple Example'),
    h1('A Simple Example'),
    start_form,
    "What's your name? ",textfield('name'),
    p,
    "What's the combination?",
    p,
    checkbox_group(-name=>'words',
                   -values=>['eenie','meenie','minie','moe'],
                   -defaults=>['eenie','minie']),
    p,
    "What's your favorite color? ",
    popup_menu(-name=>'color',
               -values=>['red','green','blue','chartreuse']),
    p,
    submit,
    end_form,
    hr;
   if (param()) {
    print
        "Your name is",em(param('name')),
        p,
        "The keywords are: ",em(join(", ",param('words'))),
        p,
        "Your favorite color is ",em(param('color')),
        hr;
   }
   print end_html;

Make it executable (chmod 700), and invoke it from a browser with the URL http://192.168.2.7/cgi-bin/test.cgi. It should display a form. When you press Submit Query, you should see the data you entered at the bottom.

To use the FTP server, it is necessary to first run inetd. You can run it manually or from a startup script with CerfCube# inetd. Now, you can copy any file to /home/ftp/ and from your development system connect to your FTP server with devsys# ftp 192.168.2.7. Now you have a working FTP server.

Ricardo Restrepo has been working with Linux for seven years and with embedded systems under Linux for one year. He enjoys learning living and extinct human and computer languages, reading classics in their original languages when possible, listening to Bach and jogging.

Load Disqus comments