Programming Tips...

In this initial column, I'll explore porting programs from other Un*x versions to Linux. Porting Un*x applications to Linux is best done, as a general rule, by porting the application to some standard which Linux follows. This way, not only will Linux users benefit from your port, but so will users of other operating systems which follow the same standards. As always, there are some exceptions.
Broad Hints

When you write a new application, trying to make as much of the application as possible follow the POSIX standard will generally provide the highest level of portability. If an application “tracks,” or follows the rules of, POSIX, it will compile without changes on almost every known version and clone of Un*x, and on Windows NT, OS/2 2.x, and recent versions of VMS, as well. If some system-dependent code must be written, it can be isolated and clearly marked, making it possible for the next person who ports the application to a new platform to port it much more easily. And programmers on non-POSIX-compliant platforms will probably know their platforms limitations, and know what to do to port your application to their platform.

Unfortunately, the current version of the POSIX standard is showing its age a little. To make a sufficiently powerful application, you generally need to use a more powerful standard, on which defines facilities missing in POSIX. In general, if your program compiles under System V (often referred to as SYSV) or BSD systems, it will probably compile without change under Linux. System V compatibility is enabled default in Linux, and I will show later in this article how to enable BSD compatibility. In general, the Linux libraries, combined with GCC, provide one of the easiest platforms available to port to. In fact, the greatest challenge in writing applications on Linux is to not use all the facilities available on Linux. To do so would make it difficult to port the application to other platforms, even though it would make the application easier to write originally for Linux, because many of these rich facilities are not available on other platforms. Some say that Linux is a better platform to port to than to port from.

For the future, a specification called “Spec 1170” is being written, and all operating systems will have to follow it to be called “UNIXtm”. It is more powerful that most previous un*x specifications—powerful enough that common applications will not have to violate it to get real work done and will be widely followed, as most Un*x vendors have already pledged to support it. Spec1170 is being developed by X/Open, developers of the current XPG3 specification. Linux will eventually comply with most, if not all, of Spec 1170, but the developers will not commit to it without seeing the final standard.

Specific Issues

There are essentially three areas in Linux which cause problems porting, two of which are not avoidable, and the other of which it would be undesirable to avoid.

Conflicting standards

In most cases, it has been possible to make library functions compatible with both SYSV and BSD, but in some cases, standards simply conflict. When this happens, the POSIX behavior is chosen, if POSIX defines it. (If POSIX does not define it, the most reasonable behavior is chosen, or some other standard behavior is used.) For instance, the behavior of signals has changed from one version of unix to another.

Signals in early versions of un*x were unreliable. Whenever a signal was called, the signal handler was uninstalled, and so the first thing that most signal handlers did was reinstall themselves, leading to code like:

void sigfoo_handler(int foo) {
    signal(SIGFOO, sigfoo_handler); /* Rest of signal handler */

However, if another SIGFOO signal was received after the signal handler had been scheduled to run, and before signal() had been called, the default behavior for the signal would happen (perhaps it was just ignored, or perhaps the program was terminated, depending on the signal), or if the new signal was received before the signal handler has returned, the signal could be lost completely. Later, reliable signals were introduced to solve these problems, but SYSV and BSD took different routes.

BSD introduced a sigaction() function to replace the old signal() function, and then re-implemented signal() in terms of sigaction(), but changed the semantics of signal() so that a signal handler installed with the signal() function stays installed. SYSV kept the old version of signal() which uninstalls itself, and introduced a whole mess of different functions for dealing with reliable signals. POSIX uses sigaction(). Linux follows POSIX, but can provide BSD signals if the BSD environment is selected, as described below.

Terminal handling also varies, and BSD terminal handling is significantly different from SYSV and POSIX, although SYSV and POSIX terminal handling are similar enough that both interfaces are easily provided by Linux by using the same mechanism in the kernel. The SYSV scheme is known as termio, and the POSIX scheme is known as termio. The older BSD scheme is known as sgtty. When porting a BSD application, you may notice compiler warnings and errors referring to sgtty.h, TIOCGETP, TIOCSETP, TIOCFLUSH, RAW, and other similar things.

To compile applications written specifically for the BSD platform, simply compile with the -I/usr/include/bsd flag, and link the application with the -lbsd flag. This makes BSD terminal handling ioctl()'s work, and makes signal() install the reliable signals that “recent” BSD code expects.