ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Big Scary Daemons

Modifying a Port

01/25/2001

Also from Big Scary Daemons:

Running Commercial Linux Software on FreeBSD

Building Detailed Network Reports with Netflow

Visualizing Network Traffic with Netflow and FlowScan

Monitoring Network Traffic with Netflow

Information Security with Colin Percival

While the FreeBSD ports collection does a wonderful job of making thousands of software packages easy to install, it doesn't cover every possible situation. If you're unfamiliar with ports, please take a look at the earlier articles in this series; ports are one of FreeBSD's greatest contributions to open source. An administrator can customize a port, generally by doing a make patch, manually modifying the source code, and continuing with make all install. If you're going that far, however, why not take things a step further and return a patch back to the FreeBSD Ports Project?

If you're considering working on the ports system, you should start by reading the Porter's Handbook. Reading ports (7) is also highly recommended. You also need to be familiar enough with the software you want to port to recognize correct and incorrect behavior; "it compiles, ship it" is not valid in the BSD world.

This article is not intended to replace the Porter's Handbook; all I'm trying to do is show how straightforward port modification can be. If you have a problem with your port, be sure to refer to the Porter's Handbook.

I recently discovered ACID, a wonderful web-based front end to the Snort intrusion detection system. (For those unfamiliar with Snort, it watches all traffic across or through an interface and logs unusual or dangerous traffic patterns that might represent an intrusion.) ACID requires Snort to log to a MySQL database. The port doesn't do this by default, but incident.org has a nice, detailed explanation of how to set it up. This is a prime candidate for a port.

A week after I promised an article on port modification, the Snort port was updated to include the functionality discussed below (as well as other database systems). Since I don't have another port to change, and don't have time to find a port that needs modifications to match my skills, I'm going to pretend this didn't happen yet. The FreeBSD porting tools, techniques, and guidelines demonstrated here are all valid.

Start by installing the software you want to use, and confirming that it works properly. Take notes of any changes you have to make. In this case I installed MySQL, built a Snort binary linked with it, and confirmed that it could log intrusion events to a database. I also had to build Nessus to generate said intrusion events, but that's not a requirement for the building side of the port.

In the case of Snort, the only changes needed were to require mysql-client and to add the path to the mysql includes and libraries to the configure script.

If I wanted to just build this in my environment and abandon the rest of the FreeBSD world to fend for itself, I could do this on the command line with:

cd /usr/ports/security/snort
make CONFIGURE_ARGS+=--with-mysql-includes=/usr/local/include/mysql \
  --with-mysql-libraries=/usr/local/lib/mysql all install

If I already had the mysql client installed, I'd be finished.

To submit patches to the ports project, however, I need to edit the Makefile. First, read the Porters Handbook. The whole book is full of useful information, and well worth a read. Chapter 4 contains Makefile information. A brief check shows that to make Snort depend on MySQL, we need two variables: CONFIGURE_ARGS and LIB_DEPENDS.

Let's tackle the configure arguments first. We've already discussed the command-line arguments needed to compile Snort against MySQL. If every Snort system needed MySQL, and every FreeBSD system was identical, and we wanted to totally hose support for ever sharing these changes to either NetBSD or OpenBSD, we could just add this to the makefile.

CONFIGURE_ARGS+=--with-mysql-includes=/usr/local/include/mysql \
                --with-mysql-libraries=/usr/local/lib/mysql

Some FreeBSD installations put the ports collection elsewhere. NetBSD puts their "ports" collection under /usr/pkgsrc. To make maintaining these systems easier, the ports collection uses a variety of environment variables. You'll find them in the file /usr/ports/Mk/bsd.ports.mk. Use these variables whenever possible.

LOCALBASE stores the path where ports install their binaries. On a standard FreeBSD system, this defaults to /usr/local. If a user installs their software elsewhere, they need to set this variable appropriately. Using LOCALBASE, our change now looks like:

CONFIGURE_ARGS+= --with-mysql-includes=${LOCALBASE}/include/mysql \
                 --with-mysql-libraries=${LOCALBASE}/lib/mysql

The real world isn't that simple, however. One of the ports system's great strengths is its ability to install dependencies. We have to add information about that dependency.

LIB_DEPENDS+=mysqlclient.6:${PORTSDIR}/databases/mysql322-client

The syntax of LIB_DEPENDS is quite straightforward; the first term is the name of the shared library, without the preceding lib or the so. (In this case, the actual name of the shared library is libmysqlclient.so.6 .) You can get this name from ldconfig -r. The second term is the location of the port that installs this library. When the "make" process cannot find the library, it builds the port in this directory.

More properly, ports lead and trail options with Makefile .if statements. This initially gave me:

..if defined(USE_MYSQL)
CONFIGURE_ARGS+=--with-mysql-includes=${LOCALBASE}include/mysql \
                --with-mysql-libraries=${LOCALBASE}lib/mysql
LIB_DEPENDS+=mysqlclient.6:${PORTSDIR/databases/mysql322-client
..endif

If you've spent any time looking at port Makefiles, this should look very familiar. But there's only one way to be sure.

# cd /usr/ports/security/snort
# make USE_MYSQL=YES
===>  Extracting for snort-1.6.3
>> Checksum OK for snort-1.6.3.tar.gz.
===>   snort-1.6.3 depends on executable: mysqlclient.6 - not found
===>    Verifying install for mysqlclient.6 in 
     >> No directory for mysqlclient.6.  Skipping..

Skipping? What do you mean, skipping? Four lines into the port, and things already went askew! I let the make continue, and it died with:

configure:2336: checking for /usr/localinclude/mysql/mysql.h
configure:2346: cc -E  -I/usr/include conftest.c >/dev/null 2>conftest.out
configure:2342: /usr/localinclude/mysql/mysql.h: No such file or directory
configure: failed program was:
#line 2341 "configure"
#include "confdefs.h"
#include 
(end of "config.log")
*** Error code 1

Stop in /usr/ports/security/snort.
*** Error code 1

The first line in the snippet above shows an obvious mistake: There's no such place as /usr/localinclude; /usr/local/include exists. Why would this happen? My patch isn't missing any slashes, after all.

It is missing a closing bracket, however. Take a close look at the LIB_DEPENDS line. PORTSDIR should have both an opening and a closing bracket.

With this correction, the port builds, installs, and deinstalls correctly. This isn't all you need for a port, however. According to the Porter's Handbook, your modified port needs to successfully pass the following tests, in order:

    make install
    make package
    make deinstall
    pkg_add package-name
    make deinstall
    make reinstall
    make package

If you can run all these commands, in this order, without errors, your port is in decent shape. You might have other errors, however.

FreeBSD includes portlint, a tool for checking port integrity and format. A successful portlint run doesn't guarantee that the port is correct. But a port that fails a portlint check almost certainly isn't correct. You'll find portlint under /usr/ports/devel/portlint.

# portlint
OK: checking /usr/ports/security/snort/pkg-comment.
OK: checking /usr/ports/security/snort/pkg-descr.
OK: checking Makefile.
WARN: Makefile 22: use tab (not space) to make indentation
FATAL: contiguous blank lines (> 1 lines) found in Makefile at line 24.
OK: checking /usr/ports/security/snort/distinfo.
1 fatal errors and 1 warnings found.
turtledawn/usr/ports/security/snort;

This is simple enough to fix. I edited the Makefile to make the appropriate changes and reran portlint:

# portlint
OK: checking /usr/ports/security/snort/pkg-comment.
OK: checking /usr/ports/security/snort/pkg-descr.
OK: checking Makefile.
OK: checking /usr/ports/security/snort/distinfo.
looks fine.
#

My modified port passes the test sequence recommended in the Porter's Handbook, and portlint says it looks good. It's time to submit.

First, prepare a patch.

diff -c Makefile.orig Makefile > patch

Redirect the diff output to a file, rather than cutting and pasting. A cut-and-paste will put all those nasty spaces back in, after all.

To officially submit your patch, use send-pr. Give it a category of "ports" and class it a change-request. If an individual is listed as maintainer, you should also contact him with your patch. It's the maintainer's responsibility to approve or reject any changes to his port.

At this point, follow the standard FreeBSD PR process. If your PR isn't closed in a few weeks, either with a commit or an explanation, contact ports@freebsd.org and politely bring it to their attention. The ports group is among the busiest of the FreeBSD teams, and like any group of volunteers they can easily miss something in the flood of PRs.

Eventually, your name will appear on a FreeBSD patch. Thousands of people will benefit from a bit of your skull sweat. Be careful, though; submit too many ports, and the ports team will inflict a commit bit on you.

Michael W. Lucas


Read more Big Scary Daemons columns.

Discuss this article in the Operating Systems Forum.

Return to the BSD DevCenter.

Copyright © 2009 O'Reilly Media, Inc.