Creating Integrated High Quality Linux Applications
IBM Linux Impact Team
Copyright © 2002 by Avi Alkalay
Linux is becoming more and more popular, and many Software vendors are porting their products from other platforms. This article tries to clarify some issues and give tips on how to create Linux applications highly integrated to the Operating System, security, and ease of use.
The examples apply to Red Hat Linux, and should be compatible with other distributions based on Red Hat (Conectiva, Turbolinux, Caldera, PLD, Mandrake, etc).
User Friendly: Guaranteed Success
The user-friendly concept is misassociated with a good GUI (graphical user interface). In fact, it is much beyond that. In systems like Linux (with more server-like characteristics), the user measures how easy software is mainly in the installation and initial configuration. He can forget how easy it was to install and use a certain product, but he will never forget that a software package has a complex configuration and installation process. A migration or new installation allways will be a nightmare, making the user avoid it.
Embrace the Install-and-Use Paradigm
Imagine you'll install that expensive product your company bought from ACME, and realize you'll have to do the following:
Sound familiar? Who never faced this sad situation, that induces the user to make mistakes? If your product's installation process sound like Uncompress-Copy-Configure-Configure More-Use, like this one, you have a problem, and the user won't like it.
Users like to feel that your Product integrates well with the OS. You should not demand that the OS adapt itself to your Product (changing environment variables, etc). It must let the user Install-and-Use.
The Install-And-Use glory is easily achieved using a 3 ingredients receipe:
We'll discuss here what these are ingredients and how to implement them.
The Four Universal Parts of Any Software
The file set of any application software -- graphical, server-side, commercial, open/free, monolithic, etc. -- always has four parts:
1st: The Software on its own -- the body
The executables, libraries, static-data files, examples, manuals and documentation, etc. Regular users must have read-only access to these files. They are changed only when the system administrator makes an upgrade in this Software.
2nd: Configuration files -- the soul
These are files that define how the software will run, how to use the content, manage security, maximize performance, etc. Without them, the software on its own is usually useless.
Depending on your software, specific privileged users may change these files, to make the software behave as they want.
It is important to provide documentation about the configuration files.
This is what receives all the user attention. It is what the user delegated to be managed by your product. It is what makes a user throw away your product and use the competitors', if it gets damaged: the tables of a database system, the documents for a text editor, the images and HTML pages of a web-server, the servlets and EJBs of an Application Server, etc.
4th: Logs, dumps, etc.
Server software uses these as access logs, trace files, for problem determination, temporary files, etc. Other types of software also use these files, but it is less common.
It is the last class of file that many times is the biggest problem generator for a system administrator, because their volume can surpass even the content size. Due this fact, it is important for you to establish some methodology or facility for this issue, while you are in design time.
Let's see how universal this concept is by analyzing some types of software:
Table 1. Universality of Four Parts
Note that the "Software on its Own" category contains all your product business logic, which could be useless if you hadn't a configuration to define how to work with a data bundle, provided by the user. Configurations are what connect your product to the user.
We can use a metaphor about a Sculptor (business logic), that needs Bronze (content) and a Theme or Inspiration (configuration) from a Mecenas (user), to produce a beautiful work (content). He makes annotations in his Journal (logs) about his day-by-day activities, to report to his Mecenas (user).
The Importance of Clear Separation Between Four Parts
OK, so let's be more practical. The fact is, if we correctly use the universal parts concept, we greatly improve the quality of our product. We'll do that simply separando, encapsulating each one of these parts in different system directories (having only different files for each part is not sufficient). There is a standard called FHS that defines the Linux directories for each part, and we'll discuss it later.
But now, let's see the value of this separation to the user:
Let's conduct an exercise with separation, using as an example a system called "MySoftware," in which the business logic is in Example 1 and the configuration is in Example 2.
Example 1. A Shell program refering an external configuration file
1 Definition of the configuration file name.
2 Definition of some static parameters.
3 The configuration is readed from an external file, if exists.
4 After reading the configuration file, all content directories -- user's plus product's -- go together in the $CONTENT_PATH that will be used from now on.
Example 2. File containing only the configurations for MySoftware
One Body, Many Souls
When I was a system administrator for IBM e-business Hosting Services, I was fascinated by Apache's flexibility in letting us do things like this:
bash# /usr/sbin/httpd &
If we don't pass any parameter (like the first example), Apache loads its default, hardcoded configuration file from /etc/httpd/conf/httpd.conf. We built other configs, one for each customer, with a completelly different structure, IP address, loaded modules, content directory, passwords, domains, log strategy, etc.
This same concept is used by a text editor on a multiuser desktop (like Linux). When the code is loaded, it looks for a configuration file on the user's $HOME, and depending who invoked it (user A or B), it will appear differently because each user has his own personal configuration.
The obvious conclusion is that the software's body (business logic) is purely and completely oriented by its manipulator's spirit (configuration). But the competitive advantage falls on how easily we switch from one spirit to another, as in Apache's example. It is very useful to provide this to your user. You'll be letting him create intimacy, reliability, and comfort with your product.
We used this approach with many different software applications during my e-business Hosting time, and it was extremely useful for maintenance, etc. In a version migration we had total control over where were each of its parts, and upgraded and downgraded software with no waste of time, with obvious success.
But there were some products that refused to work this way. They had so many hardcoded parameters that we couldn't see what divided the body from its spirit (or other parts). These applications were marked as "bad guys" and discarded and replaced as soon as possible.
We concluded that the "good guys" applications were intuitivelly blessed by their developers' four parts vision. And they made our life easier. In fact, that is when we formulated this theory, which continues to prove itself.
Do you want to deploy bad guy or good guy software?
Linux Directory Hierarchy: Oriented to the Software Parts
Until now, all discussion has been OS independent. On Linux, the Four Software Parts theory is expressed in its directory structure, which is classified and documented in the Filesystem Hierarchy Standard. The FHS is part of the LSB (Linux Standard Base), which is a good thing because all the industry is moving toward it, and it should be on the minds of all distributions. FHS defines in which directories each piece of Apache, Samba, Mozilla, KDE and your software must go. That's reason enough to use it, but there are other reasons as well:
This last reason already justifies FHS adoption, so always be guided by the FHS!
Let's summarize what the FHS has to say about Linux directories:
Linux system directories
Directory for the executables that are accessed by all users (everybody has this directory in his $PATH). The main files of your software will probably be here. You should never create a subdirectory under this directory.
Like /usr/bin, but here you'll find only boot process-vital executables that are simple and small. Your software (being high-level) probably doesn't have anything to install here.
Like /usr/bin, but contains only the executables that must be accessed by the administrator (root user). Regular users should never have this directory in their $PATH. If your software is a daemon, this is the directory for some of its executables.
Like /usr/sbin, but only for the boot process-vital executables, and some that will be accessed by sysadmin for some system maintenance. Commands like fsck (filesystem check), init (father of all processes), ifconfig (network configuration), mount, etc., can be found here. It is the system's most vital directory in many ways.
Contains dynamic libraries and support static files for the executables at /usr/bin and /usr/sbin. You may create a subdirectory such as /usr/lib/myproduct to contain your helper files or dynamic libraries that will be accessed only by your software without user intervention. A subdirectory here may be used as a container for plugins and extensions.
Like /usr/lib but contains dynamic libraries and support static files needed in the boot process. You'll never find an executable at /bin or /sbin that needs a library that is outside this directory. Kernel modules (device drivers) are under /lib.
Contains configuration files. If your software uses several files, put them in a subdirectory such as /etc/myproduct/.
The name comes from "variable", because everything that is under this directory changes a frequently. Often, /var is mounted in a separate high-performance partition. In /var/log logfiles grow. For web content we use /var/www, and so on.
Contains the users' (real human beings') home directories. Your software package should never install files here (during installation). If your business logic requires a special UNIX user (not a human being) to be created, you should assign him a home directory under /var or some other place outside /home. Do not forget that.
The "share" word is used because what is under /usr/share is platform independent, and can be shared among several machines thru a network filesystem. Portanto this is the place for manuals, documentations, examples etc.
These are obsolete folders. When UNIX didn't have a package system (like RPM), sysadmins needed to separate an optional (or local) application from the main OS. These were the directories used for that.
You may think is a bad idea to break your software (as a whole) in many pieces, instead of keeping it all under a self-contained directory. But a package system (RPM) has a database that manages it all for you in a very professional way, taking care of configuration files, directories etc. And if you spread your software using the FHS, beyond the user friendliness, you'll bring an intuitive way for the sysadmin configure it, with better performance and security.
Examples Using the FHS
Now that we know where each part of our software must be installed, lets review the Universal Parts Table applied to the FHS.
Table 2. Same Software, applying FHS
Friendly Advice: Never use /opt or /usr/local
This is a very polemic subject, and that is why this is the most important section in this document. After almost ten years of UNIX experience I can beat any /usr/local-prone argument. I'll try to organize some ideas here.
It is very important to Linux evolution and popularization (sobretudo in the desktop battlefield), that developers stop using these directories, and start using the FHS. After reading this section, if you still think these directories are good business, please drop me an e-mail.
Products that are entirely installed under one directory use the self-contained approach, which has several problems:
These directories exists for historical reasons, and for compatibility with other UNICES, from the era when we didn't have a package management system, so the sysadmin needed to segregate the products to keep control. And the last item shows us that even if you try, you'll not be able to segragate your product. You'll only make it distant and impractical to the user.
You don't have to be afraid of spreding your files according to FHS because RPM will keep an eye on them.
Many developers believe that the "self-contained" approach lets them work with several versions of the same product, for testing purposes or whatever. Yes, feel free to agree with this or any good reason on the planet. But remember that a High Quality Software (or Commercial Grade Software) Product objective is to be practical for the final user, even if inconvenient for developers and testers. Invite yourself to visit an inexperienced user (but potential customer) and watch him installing your product.
If you have a business requirement that the user work with several versions of your Product simultaneously (or have some other reason), make a relocatable package, which is described in the book, Maximum RPM. Be also aware of the implications of using this feature, described in the same book.
Note that distributions like Red Hat and derivatives always use the FHS, instead of /opt or /usr/local. Read what Red Hat says about this subject, and think about it.
The makefiles of an open source program that is portable to other UNICES must have the standard installation in /usr/local for compatibility reasons. But must also give the option, and encourace the packager, to create the package using FHS specifications.
Provide Architecture for Extensions and Plugins
You'll probably let other software vendors plug extensions onto your product. Since you are the author of the initial package, is your responsibility to organize it in such a way that the user can simply install the extension RPM and use it, without forcing him modify any configuration file. Is is again the famous Install-and-Use concept that guarantees ease-of-use.
An extension is nothing more than some files in the proper format (DLLs that implement the API your software defined), put in the right directory (where your software looks for extensions). We see many applications that request the user to change configuration files to "declare" the presence of a new plugin.
The most important thing to consider in your plugin architecture is to not share files between plugins and your software. You should provide an architecture where plugins will fully install and uninstall themselves by simply putting and removing files in specific directories, documented in your program. Good candidates are /usr/lib/myproduct/plugins as the plugins directory, and /etc/myproduct/plugins as the plugins configuration files directory. Your software and plugins must be sufficiently inteligent to know how to find files, especially configurations, in these directories. Using this approach, no post-install procedure is required from the user or from the plugin provider.
Plugins in the Abstract
I would like to close this subject by inviting the reader to consider how any program can be thought of as an extension to the lower level software. In the same way a third party plugin is an extension to your software, your software is also an extension to the OS (lower level). This is where all the Integration (from the title of this document) magic lives. So we can apply all the ease-of-use concepts we discussed before to the plugin architecture design of your software.
Always Provide RPM Packages of Your Software
This is extremely important for many reasons:
But a good package is not only putting your files in a RPM. The FHS must be followed, configuration and documentation files must be marked as such, and pre- and post-install scripts must be robust, so that thay cannot damage the system (remember that installation processes is done by root).
Thoroughly familiarize yourself with RPM because it can bring much power and convenience to you and your users. There is a lot of documentation available about RPM on the Internet:
Software Package Modularization
You should give users the option to install only the part of your software he wants. Imagine your application has both client and server parts, which have files and libraries in common. You should break your application into three RPMs. For instance, lets say the name of your product is MyDB. You'll provide these packages:
The last two packages depend on the first. If the user is installing a client profile, he will use:
If he is installing a server profile:
This approach will help the user save disk space and be aware of how your software is organized.
Security: The Omnipresent Concept
From a very general perspective, security is synonym of maintaining order, conscience. And insecure is everything that runs counter to this idea. So besides open network ports, or weak cryptography (both of which are beyond the scope of this document), applications that require the user to use them only as root, or make him change files in protected places, is considered insecure. We can say the same for apps that fill a filesystem that is vital to the OS.
Many standards have resulted from good practices discussed and developed over a long time and lots of experience. So you should know and use them when you package your software, because they are key for you to achieve a good organization (security) level.
Graphical User Interfaces
Everybody loves graphical interfaces. Many times they make our lives easier, and in this way help to popularize software, because the learning curve becomes shallower. But for everyday use, a command at the console prompt, with many options and a good manual, becomes much more practical, making scripts easy, allowing for remote access, etc. So the suggestion is, whenever is possible, to provide both interfaces: graphical for the beginners, and the powerful command line for the expert.
KDE, GNOME, Java or Motif?
Better then a simple graphical interface is a consistent integrated desktop. And the desktops today in Linuxland are KDE and GNOME. Try always to use one of them, or both.
KDE is the most destacado, offering a truly consistent desktop, flexibility, and an extremely elegant architecture, that employs components and intercomunication, among other features. It is constantly evolving, and is developed in C++. Its applications have an familiar integrated look-and-feel. It is light and mature.
GNOME also uses the integrated desktop concept, but it is far from the maturity and ease-of-use of KDE. On the other hand, it is very well supported by the community, and substantial improvements are appearing.
Motif isn't an integrated desktop. It is a widgets library (buttons, scrollbar, etc.), plus a window-manager. It was born commercially, is mature, and is popular in commercial aplications. But Motif is considered obsolete in light of KDE and GNOME, which integrate the desktop. Motif source code has been opened by the OpenGroup and renamed OpenMotif.
Java is being used more and more for graphical interfaces, specially in server software, where the graphics are only helpers to configuration and administration.
Web Interface: Access from Anywhere
Nowadays every desktop has a browser, and if your product is a server application, the Web Interface is the right choice, because it lets a user administer it from anywhere. But keep in mind the security and organization of your CGIs, because they can become front doors for crackers. Web interface (CGI) is completely different programming paradigm. Try to understand it conceptually first, starting from "how a web-server works", "what is a URL", etc., to avoid compromising your product's security.
Wizards and Graphical Installers
Especially if it is a commercial product, your application must provide a graphical installer. Believe me, they are impressive in a demonstration, and CIOs love them.
More then just overseeing installation, a wizard helps in the initial configuration of your product, collects information such as like activation keys, and shows the developer license.
A wizard should not do more than this:
So the wizard hides the RPM installation and writes initial personalization. RPM is still responsable for putting all your software files in the correct places. This role should never fall to your installer. Think that an experienced user (there are a lot of them in the Linux world) should be able to reproduce your Product installation without the graphical help, using only RPM commands. In fact, in big data centers, where people make mass installations, a graphical installer only gets in the way.
RPM provides tools that help your graphical installer interact with it, such as the installation percentage viewer. Documentation for use can be found in the RPM manual (man rpm) and in the Maximum RPM book.
Starting Your Software Automatically on Boot
The way Linux starts (and stops) all its subsystems is very simple and modular. This lets you define initialization order, runlevels, and so on.
From BIOS to Subsystems
Lets review what happens when we boot Linux:
The runlevels mechanism lets Linux intialize itself in different ways. And also lets us change from one profile (runlevel) to another without rebooting.
The default runlevel is defined in /etc/inittab with a line like this:
Example 3. Default runlevel (3, in this case) line in /etc/inittab
Runlevels are numbers from 0 to 6 and each one of them is used following this standard:
You can switch from one runlevel to another using the telinit command. And you can see the current runlevel and the last one with the runlevel command. See below how we switch from runlevel 3 to 5.
bash# telinit 5
Subsystems examples are a web-server, data base server, OS network layer, etc. We'll not consider a user-oriented application (like a text editor) as a subsystem.
Linux provides an elegant and modular way to organize the subsystems initialization. An important fact to think about is subsystems interdependency. For instance, it makes no sense to start a web-server before basic networking subsystem is active.
Subsystems are organized under the /etc/init.d and /etc/rc.d/rcN.d directories:
All installed Subsystems put in this directory a control program, which is a script that follows a simple standard described below. This is a simplified listing of this directory:
Example 4. Subsystems installed in /etc/init.d
bash:/etc/init.d# ls -l
/etc/rc.d/rcN.d (N is the runlevel indicator)
These directories must contain only special symbolic links to the scripts in /etc/init.d. This is how it looks:
Example 5. /etc/rc3.d listing
bash:/etc/rc3.d# ls -l
So, to make your software start automatically in the boot process, it must be a subsystem. We'll see how to do this in the following section.
Turning Your Software Into a Subsystem
Your software's files will spread through the filesystems, but you'll want to provide a simple and consistent interface to let the user at least start and stop it. Subsystems architecture promotes this ease-of-use, also providing a way for it to be automatically started on system initialization. You just have to create your /etc/init.d script following a standard to make it functional.
The mysystem subsystem methods you implemented will be called by users with a service command such as this example:
Example 7. service command usage
bash# service mysystem start
You don't have to worry about managing the symbolic links in /etc/rc.d/rcN.d. The chkconfig command does that for you, based on the control comments defined in the begining of your script.
Example 8. Using the chkconfig command
bash# chkconfig --add mysystem
Read the chkconfig manual page to see what more it can do for you.
Packaging Your Boot Script
When you create the RPM, put your Subsystem script in /etc/init.d and do not include any /etc/rc.d/rcN.d link, because it is a user decision to make your subsystem automatic or not. If you include it and the user makes any change, the RPM file inventory will become inconsistent.
The symbolic links must be created and removed dynamically by the post-installation and pre-uninstallation process of your package, using the chkconfig command. This aproach guaratees 100% package and filesystem consistency.
A. Red Hat, About the Filesystem Structure
This text was taken from The Official Red Hat Linux Reference Guide
Why Share a Common Structure?
An operating system's filesystem structure is its most basic level of organization. Almost all of the ways an operating system interacts with its users, applications, and security model are dependent upon the way it stores its files on a primary storage device (normally a hard disk drive). It is crucial for a variety of reasons that users, as well as programs at the time of installation and beyond, be able to refer to a common guideline to know where to read and write their binary, configuration, log, and other necessary files.
A filesystem can be seen in terms of two different logical categories of files:
Shareable files are those that can be accessed by various hosts; unshareable files are not available to any other hosts. Variable files can change at any time without system administrator intervention (whether active or passive); static files, such as documentation and binaries, do not change without an action from the system administrator or an agent that the system administrator has placed in motion to accomplish that task.
The reason for looking at files in this way has to do with the type of permissions given to the directory that holds them. The way in which the operating system and its users need to utilize the files determines the directory where those files should be placed, whether the directory is mounted read-only or read-write, and the level of access allowed on each file. The top level of this organization (/ directory)is crucial, as the access to the underlying directories can be restricted or security problems may manifest themselves if the top level is left disorganized (security=organization) or without a widely-utilized structure.
However, simply having a structure does not mean very much unless it is a standard. Competing structures can actually cause more problems than they fix. Because of this, Red Hat has chosen the the most widely-used filesystem structure and extended it only slightly to accommodate special files used within Red Hat Linux.
B. About this Document
This document must be distributed under the terms of GNU Free Documentation License, which makes it sufficiently free. Everybody in invited to contribute to its content and ideas.
Copyright 2002, Avi Alkalay.
The original version of this document can be found online at http://avi.alkalay.net/linux/docs/HighQuality/.
It was written originally in Brazilian Portuguese, and then translated to English. SGML and the more-than-incredible DocBook was used, which made possible this document being distributed in other formats, as found on the website.
It got ready (Portuguese+English) in mid-March 2002. Everything changed after this time period is cosmetics.
I wrote it to help commercial companies and OpenSource developers make plug-and-play, easy-to-use software for Linux, and this way improve Linux usability and popularity.
All concepts (from a high level perspective) described here, can be used in any UNIX flavor, or even other OSes, like Windows. Maybe some day I'll write one of these for Windows....or Mac....
Each week, a member of the Linux community, sometimes famous, sometimes not, discusses an issue of interest in a Guest Essay. If you'd like to contribute, send your essay to us here.
|Posted 24 March 2002|