diff --git a/en_US.ISO8859-1/articles/new-users/article.sgml b/en_US.ISO8859-1/articles/new-users/article.sgml
index 92a862ed06..99f641c894 100644
--- a/en_US.ISO8859-1/articles/new-users/article.sgml
+++ b/en_US.ISO8859-1/articles/new-users/article.sgml
@@ -1,1052 +1,1052 @@
-
+
For People New to Both FreeBSD and Unix
Annelise
Anderson
andrsn@andrsn.stanford.edu
August 15, 1997
Congratulations on installing FreeBSD! This introduction
is for people new to both FreeBSD and
Un*x—so it starts with basics. It assumes you're using
version 2.0.5 or later of FreeBSD as distributed by BSDi
or FreeBSD.org, your system (for now) has a single user
(you)—and you're probably pretty good with DOS/Windows
or OS/2.
Logging in and Getting Out
Log in (when you see login: ) as a user you created during
installation or as root . (Your FreeBSD
installation will already have an account for root; root can go
anywhere and do anything, including deleting essential files, so
be careful!) The symbols &prompt.user; and &prompt.root; in the following stand for the
prompt (yours may be different), with &prompt.user; indicating an ordinary
user and &prompt.root; indicating root.
To log out (and get a new login: prompt) type
&prompt.root; exit
as often as necessary. Yes, press enter
after commands, and remember that Unix is
case-sensitive—exit , not
EXIT .
To shut down the machine type
&prompt.root; /sbin/shutdown -h now
Or to reboot type
&prompt.root; /sbin/shutdown -r now
or
&prompt.root; /sbin/reboot
You can also reboot with
Ctrl Alt Delete .
Give it a little time to do its work. This is equivalent to
/sbin/reboot in recent releases of FreeBSD
and is much, much better than hitting the reset button. You
don't want to have to reinstall this thing, do you?
Adding A User with Root Privileges
If you didn't create any users when you installed the system
and are thus logged in as root, you should probably create a
user now with
&prompt.root; adduser
The first time you use adduser, it might ask for some
defaults to save. You might want to make the default shell csh
instead of sh, if it suggests sh as the default. Otherwise just
press enter to accept each default. These defaults are saved in
/etc/adduser.conf , an editable file.
Suppose you create a user jack with
full name Jack Benimble . Give jack a
password if security (even kids around who might pound on the
keyboard) is an issue. When it asks you if you want to invite
jack into other groups, type wheel
Login group is ``jack''. Invite jack into other groups: wheel
This will make it possible to log in as
jack and use the su
command to become root. Then you won't get scolded any more for
logging in as root.
You can quit adduser any time by typing
Ctrl C ,
and at the end you'll have a chance to approve your new user or
simply type n for no. You might want to create
a second new user (jill?) so that when you edit jack's login
files, you'll have a hot spare in case something goes
wrong.
Once you've done this, use exit to get
back to a login prompt and log in as jack .
In general, it's a good idea to do as much work as possible as
an ordinary user who doesn't have the power—and
risk—of root.
If you already created a user and you want the user to be
able to su to root, you can log in as root
and edit the file /etc/group , adding jack
to the first line (the group wheel). But first you need to
practice vi , the text editor--or use the
simpler text editor, ee , installed on recent
version of FreeBSD.
To delete a user, use the rmuser
command.
Looking Around
Logged in as an ordinary user, look around and try out some
commands that will access the sources of help and information
within FreeBSD.
Here are some commands and what they do:
id
Tells you who you are!
pwd
Shows you where you are—the current working
directory.
ls
Lists the files in the current directory.
ls -F
Lists the files in the current directory with a
* after executables, a
/ after directories, and an
@ after symbolic links.
ls -l
Lists the files in long format—size, date,
permissions.
ls -a
Lists hidden dot
files with the others.
If you're root, the dot
files show up
without the -a switch.
cd
Changes directories. cd
.. backs up one level;
note the space after cd . cd
/usr/local goes there.
cd ~ goes to
the home directory of the person logged in—e.g.,
/usr/home/jack . Try cd
/cdrom , and then
ls , to find out if your CDROM is
mounted and working.
view
filename
Lets you look at a file (named
filename ) without changing it.
Try view
/etc/fstab .
:q to quit.
cat
filename
Displays filename on
screen. If it's too long and you can see only the end of
it, press ScrollLock and use the
up-arrow to move backward; you can use
ScrollLock with man pages too. Press
ScrollLock again to quit scrolling. You
might want to try cat on some of the
dot files in your home directory—cat
.cshrc , cat
.login , cat
.profile .
You'll notice aliases in .cshrc for
some of the ls commands (they're very
convenient). You can create other aliases by editing
.cshrc . You can make these aliases
available to all users on the system by putting them in the
system-wide csh configuration file,
/etc/csh.cshrc .
Getting Help and Information
Here are some useful sources of help.
Text stands for something of your
choice that you type in—usually a command or
filename.
apropos
text
Everything containing string
text in the whatis
database .
man
text
The man page for text . The
major source of documentation for Un*x systems.
man ls will tell
you all the ways to use the ls command.
Press Enter to move through text,
Ctrl b
to go back a page,
Ctrl f
to go forward, q or
Ctrl c
to quit.
which
text
Tells you where in the user's path the command
text is found.
locate
text
All the paths where the string
text is found.
whatis
text
Tells you what the command
text does and its man page.
Typing whatis * will tell you about all
the binaries in the current directory.
whereis
text
Finds the file text , giving
its full path.
You might want to try using whatis on
some common useful commands like cat ,
more , grep ,
mv , find ,
tar , chmod ,
chown , date , and
script . more lets you
read a page at a time as it does in DOS, e.g., ls -l |
more or more
filename . The
* works as a wildcard—e.g., ls
w* will show you files beginning with
w .
Are some of these not working very well? Both
locate and whatis depend
on a database that's rebuilt weekly. If your machine isn't
going to be left on over the weekend (and running FreeBSD), you
might want to run the commands for daily, weekly, and monthly
maintenance now and then. Run them as root and give each one
time to finish before you start the next one, for now.
&prompt.root; periodic daily
output omitted
&prompt.root; periodic weekly
output omitted
&prompt.root; periodic monthly
output omitted
If you get tired of waiting, press
Alt F2 to
get another virtual console , and log in
again. After all, it's a multi-user, multi-tasking system.
Nevertheless these commands will probably flash messages on your
screen while they're running; you can type
clear at the prompt to clear the screen.
Once they've run, you might want to look at
/var/mail/root and
/var/log/messages .
Running such commands is part of system
administration—and as a single user of a Unix system,
you're your own system administrator. Virtually everything you
need to be root to do is system administration. Such
responsibilities aren't covered very well even in those big fat
books on Unix, which seem to devote a lot of space to pulling
down menus in windows managers. You might want to get one of
the two leading books on systems administration, either Evi
Nemeth et.al.'s UNIX System Administration
Handbook (Prentice-Hall, 1995, ISBN
0-13-15051-7)—the second edition with the red cover; or
Æleen Frisch's Essential System
Administration (O'Reilly & Associates, 1993,
ISBN 0-937175-80-3). I used Nemeth.
Editing Text
To configure your system, you need to edit text files. Most
of them will be in the /etc directory; and
you'll need to su to root to be able to
change them. You can use the easy ee , but in
the long run the text editor vi is worth
learning. There's an excellent tutorial on vi in
/usr/src/contrib/nvi/docs/tutorial if you
have that installed; otherwise you can get it by ftp to
ftp.cdrom.com in the directory
FreeBSD/FreeBSD-current/src/contrib/nvi/docs/tutorial.
Before you edit a file, you should probably back it up.
Suppose you want to edit /etc/rc.conf . You
could just use cd /etc to get to the
/etc directory and do:
&prompt.root; cp rc.conf rc.conf.orig
This would copy rc.conf to
rc.conf.orig , and you could later copy
rc.conf.orig to
rc.conf to recover the original. But even
better would be moving (renaming) and then copying back:
&prompt.root; mv rc.conf rc.conf.orig
&prompt.root; cp rc.conf.orig rc.conf
because the mv command preserves the
original date and owner of the file. You can now edit
rc.conf . If you want the original back,
you'd then mv rc.conf rc.conf.myedit
(assuming you want to preserve your edited version) and
then
&prompt.root; mv rc.conf.orig rc.conf
to put things back the way they were.
To edit a file, type
&prompt.root; vi filename
Move through the text with the arrow keys.
Esc (the escape key) puts vi
in command mode. Here are some commands:
x
delete letter the cursor is on
dd
delete the entire line (even if it wraps on the
screen)
i
insert text at the cursor
a
insert text after the cursor
Once you type i or a ,
you can enter text. Esc puts you back in
command mode where you can type
:w
to write your changes to disk and continue
editing
:wq
to write and quit
:q!
to quit without saving changes
/text
to move the cursor to text ;
/Enter (the enter key)
to find the next instance of
text .
G
to go to the end of the file
n G
to go to line n in the
file, where n is a
number
Ctrl L
to redraw the screen
Ctrl b and
Ctrl f
go back and forward a screen, as they do with
more and view .
Practice with vi in your home directory by
creating a new file with vi filename
and adding and deleting text, saving the file, and calling it up
again. vi delivers some surprises because it's
really quite complex, and sometimes you'll inadvertently issue a
command that will do something you don't expect. (Some people
actually like vi —it's more powerful than DOS
EDIT—find out about the :r command.) Use
Esc one or more times to be sure you're in command
mode and proceed from there when it gives you trouble, save
often with :w , and use :q! to get out
and start over (from your last :w ) when you need
to.
Now you can cd to /etc ,
su to root, use vi to edit the file
/etc/group , and add a user to wheel so the
user has root privileges. Just add a comma and the user's login
name to the end of the first line in the file, press
Esc , and use :wq to write the file to
disk and quit. Instantly effective. (You didn't put a space
after the comma, did you?)
Printing Files from DOS
At this point you probably don't have the printer working,
so here's a way to create a file from a man page, move it to a
floppy, and then print it from DOS. Suppose you want to read
carefully about changing permissions on files (pretty
important). You can use the command man chmod to read about it.
The command
&prompt.user; man chmod | col -b > chmod.txt>
will remove formatting codes and send the man page to the
chmod.txt file instead of showing it on
your screen. Now put a dos-formatted diskette in your floppy
drive a, su to root, and type
&prompt.root; /sbin/mount -t msdos /dev/fd0 /mnt>
to mount the floppy drive on
/mnt .
Now (you no longer need to be root, and you can type
exit to get back to being user jack) you can go to
the directory where you created chmod.txt and copy the file to
the floppy with:
&prompt.user; cp chmod.txt /mnt>
and use ls /mnt to get a directory
listing of /mnt , which should show the file
chmod.txt .
You might especially want to make a file from
/sbin/dmesg by typing
&prompt.user; /sbin/dmesg > dmesg.txt>
and copying dmesg.txt to the floppy.
/sbin/dmesg is the boot log record, and it's
useful to understand it because it shows what FreeBSD found when
it booted up. If you ask questions on
freebsd-questions@FreeBSD.org or on a USENET
group—like FreeBSD isn't finding my tape drive,
what do I do?
—people will want to know what
dmesg has to say.
You can now dismount the floppy drive (as root) to get the
disk out with
&prompt.root; /sbin/umount /mnt>
and reboot to go to DOS. Copy these files to a DOS
directory, call them up with DOS EDIT, Windows Notepad or
Wordpad, or a word processor, make a minor change so the file
has to be saved, and print as you normally would from DOS or
Windows. Hope it works! man pages come out best if printed
with the dos print command. (Copying files from
FreeBSD to a mounted dos partition is in some cases still a
little risky.)
Getting the printer printing from FreeBSD involves creating
an appropriate entry in /etc/printcap and
creating a matching spool directory in
/var/spool/output . If your printer is on
lpt0 (what dos calls LPT1 ), you may
only need to go to /var/spool/output and
(as root) create the directory lpd by typing:
mkdir lpd , if it doesn't already exist.
Then the printer should respond if it's turned on when the
system is booted, and lp or lpr should send a file to the
printer. Whether or not the file actually prints depends on
configuring it, which is covered in the FreeBSD handbook.
Other Useful Commands
df
shows file space and mounted systems.
ps aux
shows processes running. ps ax is a
narrower form.
rm filename
remove filename .
rm -R dir
removes a directory dir and all
subdirectories—careful!
ls -R
lists files in the current directory and all
subdirectories; I used a variant, ls -AFR >
where.txt , to get a list of all the files in
/ and (separately)
/usr before I found better ways to
find files.
passwd
to change user's password (or root's password)
man hier
man page on the Unix file system
Use find to locate filename in
/usr or any of its subdirectories
with
&prompt.user; find /usr -name "filename>">
You can use * as a wildcard in
"filename " (which should be in
quotes). If you tell find to search in /
instead of /usr it will look for the
file(s) on all mounted file systems, including the CDROM and the
dos partition.
An excellent book that explains Unix commands and utilities
is Abrahams & Larson, Unix for the
Impatient (2nd ed., Addison-Wesley, 1996).
There's also a lot of Unix information on the Internet. Try the
Unix Reference
Desk .
Next Steps
You should now have the tools you need to get around and
edit files, so you can get everything up and running. There is
a great deal of information in the FreeBSD handbook (which is
probably on your hard drive) and FreeBSD's web site . A
wide variety of packages and ports are on the CDROM as well
as the web site. The handbook tells you more about how to use
them (get the package if it exists, with pkg_add
/cdrom/packages/All/packagename , where
packagename is the filename of the
package). The cdrom has lists of the packages and ports with
brief descriptions in cdrom/packages/index ,
cdrom/packages/index.txt , and
cdrom/ports/index , with fuller descriptions
in /cdrom/ports/*/*/pkg/DESCR , where the
* s represent subdirectories of kinds of
programs and program names respectively.
If you find the handbook too sophisticated (what with
lndir and all) on installing ports from the cdrom,
here's what usually works:
Find the port you want, say kermit . There will
be a directory for it on the cdrom. Copy the subdirectory to
/usr/local (a good place for software you
add that should be available to all users) with:
&prompt.root; cp -R /cdrom/ports/comm/kermit /usr/local>
This should result in a
/usr/local/kermit subdirectory that has all
the files that the kermit subdirectory on the
CDROM has.
Next, create the directory
/usr/ports/distfiles if it doesn't already
exist using mkdir . Now check check
/cdrom/ports/distfiles for a file with a
name that indicates it's the port you want. Copy that file to
/usr/ports/distfiles ; in recent versions
you can skip this step, as FreeBSD will do it for you. In the
case of kermit , there is no distfile.
Then cd to the subdirectory of
/usr/local/kermit that has the file
Makefile . Type
&prompt.root; make all install>
During this process the port will ftp to get any compressed
files it needs that it didn't find on the cdrom or in
/usr/ports/distfiles . If you don't have
your network running yet and there was no file for the port in
/cdrom/ports/distfiles , you will have to
get the distfile using another machine and copy it to
/usr/ports/distfiles from a floppy or your
dos partition. Read Makefile (with cat
or more or view ) to find out where to go
(the master distribution site) to get the file and what its name
is. Its name will be truncated when downloaded to DOS, and
after you get it into /usr/ports/distfiles
you'll have to rename it (with the mv command) to
its original name so it can be found. (Use binary file
transfers!) Then go back to
/usr/local/kermit , find the directory with
Makefile , and type make all
install .
The other thing that happens when installing ports or
packages is that some other program is needed. If the
installation stops with a message can't find
unzip or whatever, you might need to install the
package or port for unzip before you continue.
Once it's installed type rehash to make FreeBSD
reread the files in the path so it knows what's there. (If you
get a lot of path not found messages when you use
whereis or which, you might want to make additions
to the list of directories in the path statement in
.cshrc in your home directory. The path
statement in Unix does the same kind of work it does in DOS,
except the current directory is not (by default) in the path for
security reasons; if the command you want is in the directory
you're in, you need to type ./ before the
command to make it work; no space after the slash.)
You might want to get the most recent version of Netscape
from their ftp site .
(Netscape requires the X Window System.) There's now a FreeBSD
version, so look around carefully. Just use gunzip
filename and tar xvf
filename on it, move the binary to
/usr/local/bin or some other place binaries
are kept, rehash , and then put the following lines
in .cshrc in each user's home directory or
(easier) in /etc/csh.cshrc , the
system-wide csh start-up file:
setenv XKEYSYMDB /usr/X11R6/lib/X11/XKeysymDB
setenv XNLSPATH /usr/X11R6/lib/X11/nls
This assumes that the file XKeysymDB and the
directory nls are in
/usr/X11R6/lib/X11 ; if they're not, find
them and put them there.
If you originally got Netscape as a port using the CDROM (or
ftp), don't replace /usr/local/bin/netscape
with the new netscape binary; this is just a shell script that
sets up the environment variables for you. Instead rename the
new binary to netscape.bin and replace the
old binary, which is
/usr/local/netscape/netscape .
Your Working Environment
Your shell is the most important part of your working
environment. In DOS, the usual shell is command.com. The shell
is what interprets the commands you type on the command line,
and thus communicates with the rest of the operating system.
You can also write shell scripts, which are like DOS batch
files: a series of commands to be run without your
intervention.
Two shells come installed with FreeBSD: csh and sh. csh is
good for command-line work, but scripts should be written with
sh (or bash). You can find out what shell you have by typing
echo $SHELL .
The csh shell is okay, but tcsh does everything csh does and
- more. It It allows you to recall commands with the arrow keys
+ more. It allows you to recall commands with the arrow keys
and edit them. It has tab-key completion of filenames (csh uses
the escape key), and it lets you switch to the directory you
were last in with cd - . It's also much
easier to alter your prompt with tcsh. It makes life a lot
easier.
Here are the three steps for installing a new shell:
Install the shell as a port or a package, just as you
would any other port or package. Use
rehash and which tcsh
(assuming you're installing tcsh) to make sure it got
installed.
As root, edit /etc/shells , adding a
line in the file for the new shell, in this case
/usr/local/bin/tcsh, and save the file. (Some ports may do
this for you.)
Use the chsh command to change your
shell to tcsh permanently, or type tcsh
at the prompt to change your shell without logging in
again.
It can be dangerous to change root's shell to something
other than sh or csh on early versions of FreeBSD and many
other versions of Unix; you may not have a working shell when
the system puts you into single user mode. The solution is to
use su -m to become root, which will give
you the tcsh as root, because the shell is part of the
environment. You can make this permanent by adding it to your
.tcshrc file as an alias with
alias su su -m.
When tcsh starts up, it will read the
/etc/csh.cshrc and
/etc/csh.login files, as does csh. It will
also read the .login file in your home
directory and the .cshrc file as well,
unless you provide a .tcshrc file. This
you can do by simply copying .cshrc to
.tcshrc .
Now that you've installed tcsh, you can adjust your prompt.
You can find the details in the manual page for tcsh, but here
is a line to put in your .tcshrc that will
tell you how many commands you have typed, what time it is, and
what directory you are in. It also produces a
> if you're an ordinary user and a
# if you're root, but tsch will do that in
any case:
set prompt = "%h %t %~ %# "
This should go in the same place as the existing set prompt
line if there is one, or under "if($?prompt) then" if not.
Comment out the old line; you can always switch back to it if
you prefer it. Don't forget the spaces and quotes. You can get
the .tcshrc reread by typing
source .tcshrc .
You can get a listing of other environmental variables that
have been set by typing env at the prompt.
The result will show you your default editor, pager, and
terminal type, among possibly many others. A useful command if
you log in from a remote location and can't run a program
because the terminal isn't capable is setenv TERM
vt100 .
Other
As root, you can dismount the CDROM with
/sbin/umount /cdrom , take it out of the drive,
insert another one, and mount it with
/sbin/mount_cd9660 /dev/cd0a /cdrom assuming
cd0a is the device name for your CDROM drive. The
most recent versions of FreeBSD let you mount the cdrom with
just /sbin/mount /cdrom .
Using the live file system—the second of FreeBSD's
CDROM disks—is useful if you've got limited space. What
is on the live file system varies from release to release. You
might try playing games from the cdrom. This involves using
lndir , which gets installed with the X Window
System, to tell the program(s) where to find the necessary
files, because they're in the /cdrom file
system instead of in /usr and its
subdirectories, which is where they're expected to be. Read
man lndir .
Comments Welcome
If you use this guide I'd be interested in knowing where it
was unclear and what was left out that you think should be
included, and if it was helpful. My thanks to Eugene W. Stark,
professor of computer science at SUNY-Stony Brook, and John
Fieber for helpful comments.
Annelise Anderson,
andrsn@andrsn.stanford.edu
diff --git a/en_US.ISO8859-1/books/developers-handbook/x86/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/x86/chapter.sgml
index 174433eb55..d5b2df1551 100644
--- a/en_US.ISO8859-1/books/developers-handbook/x86/chapter.sgml
+++ b/en_US.ISO8859-1/books/developers-handbook/x86/chapter.sgml
@@ -1,6539 +1,6539 @@
x86 Assembly Language Programming
This chapter was written by G. Adam Stanislav.
Whiz Kid Technomagic
Synopsis
Assembly language programing under Unix is highly undocumented. It
is generally assumed that no one would ever want to use it because
various Unix systems run on different microprocessors, so everything
should be written in C for portability.
In reality, C portability is quite a myth. Even C programs need
to be modified when ported from one Unix to another, regardless of
what processor each runs on. Typically, such a program is full
of conditional statements depending on the system it is
compiled for.
Even if we believe that all of Unix software should be written in C,
or some other high-level language, we still need assembly language
programmers: Who else would write the section of C library
that accesses the kernel?
In this chapter I will attempt to show you
how you can use assembly language writing
Unix programs, specifically under FreeBSD.
This chapter does not explain the basics of assembly language.
There are enough resources about that (for a complete
online course in assembly language, see Randall Hyde's
Art
of Assembly Language ; or if you prefer
a printed book, take a look at Jeff Duntemann's
Assembly
Language Step-by-Step ). However,
once the chapter is finished, any assembly language programmer
will be able to write programs for FreeBSD
quickly and efficiently.
Copyright © 2000-2001 G. Adam Stanislav. All rights reserved.
The Tools
The Assembler
The most important tool for assembly language programming is the
assembler, the software that converts assembly language code
into machine language.
Two very different assemblers are available for FreeBSD. One is
as 1 ,
which uses the traditional Unix assembly language syntax. It
comes with the system.
The other is /usr/ports/devel/nasm .
It uses the Intel syntax. Its main advantage is that it
can assemble code for many operating systems. It needs
to be installed separately, but is completely free.
This chapter uses nasm
syntax because most assembly language programmers
coming to FreeBSD from other operating systems
will find it easier to understand. And, because,
quite frankly, that is what I am used to.
The Linker
The output of the assembler, like that of any
compiler, needs to be linked to form an executable file.
The standard
ld 1
linker comes with FreeBSD. It works with the
code assembled with either assembler.
System Calls
Default Calling Convention
By default, the FreeBSD kernel uses the C calling
convention. Further, although the kernel is accessed
using int 80h ,
it is assumed the program will call a function that
issues int 80h , rather than
issuing int 80h directly.
This convention is very convenient, and quite superior to the
Microsoft convention used by MS DOS .
Why? Because the Unix convention allows any program written in
any language to access the kernel.
An assembly language program can do that as well.
For example, we could open a file:
kernel:
int 80h ; Call kernel
ret
open:
push dword mode
push dword flags
push dword path
mov eax, 5
call kernel
add esp, byte 12
ret
This is a very clean and portable way of coding. If you need to
port the code to a Unix system which uses a different interrupt,
or a different way of passing parameters, all you need to change
is the kernel procedure.
But assembly language programmers like to shave off cycles. The above example
requires a call/ret combination.
We can eliminate it by
push ing an extra dword:
open:
push dword mode
push dword flags
push dword path
mov eax, 5
push eax ; Or any other dword
int 80h
add esp, byte 16
The 5 that we have placed in
EAX identifies
the kernel function, in this case open .
Alternate Calling Convention
FreeBSD is an extremely flexible system. It offers other ways of
calling the kernel. For it to work, however, the system must
have Linux emulation installed.
Linux is a Unix-like system. However, its kernel uses the same
system-call convention of passing parameters in registers
MS DOS does. As with the Unix convention,
the function number is placed in EAX .
The parameters, however, are not passed on the stack but in
EBX, ECX, EDX, ESI, EDI, EBP :
open:
mov eax, 5
mov ebx, path
mov ecx, flags
mov edx, mode
int 80h
This convention has a great disadvantage over
the Unix way, at least as far as assembly language programming
is concerned: Every time you make a kernel call
you must push the registers, then
pop them later. This makes your code
bulkier and slower. Nevertheless, FreeBSD gives
you a choice.
If you do choose the Linux convention, you must let
the system know about it. After your program is assembled and
linked, you need to brand the executable:
&prompt.user; brandelf -f Linux filename
Which Convention Should You Use?
If you are coding specifically for FreeBSD, you should always
use the Unix convention: It is faster, you can store global
variables in registers, you do not have to brand
the executable, and you do not impose the installation of
the Linux emulation package on the target system.
If you want to create portable code that can also run
on Linux, you will probably still want to give the FreeBSD
users as efficient a code as possible. I will show you
how you can accomplish that after I have explained the basics.
Call Numbers
To tell the kernel which system service you are calling,
place its number in EAX . Of course, you need
to know what the number is.
The syscalls File
The numbers are listed in syscalls .
locate syscalls finds this file
in several different formats, all produced automatically
from syscalls.master .
You can find the master file for the default Unix calling
convention in
/usr/src/sys/kern/syscalls.master .
If you need to use the other convention implemented
in the Linux emulation mode, read
/usr/src/sys/i386/linux/syscalls.master .
Not only do FreeBSD and Linux use different calling
conventions, they sometimes use different numbers for
the same functions.
syscalls.master describes how
the call is to be made:
0 STD NOHIDE { int nosys(void); } syscall nosys_args int
1 STD NOHIDE { void exit(int rval); } exit rexit_args void
2 STD POSIX { int fork(void); }
3 STD POSIX { ssize_t read(int fd, void *buf, size_t nbyte); }
4 STD POSIX { ssize_t write(int fd, const void *buf, size_t nbyte); }
5 STD POSIX { int open(char *path, int flags, int mode); }
6 STD POSIX { int close(int fd); }
etc...
It is the leftmost column that tells us the number to place in
EAX .
The rightmost column tells us what parameters to
push . They are push ed
from right to left .
For example, to open a file, we need
to push the mode first,
then flags , then the address at which
the path is stored.
Return Values
A system call would not be useful most of the time
if it did not return some kind of a value: The file
descriptor of an open file, the number of bytes read
to a buffer, the system time, etc.
Additionally, the system needs to inform us if an error
occurs: A file does not exist, system resources are exhausted,
we passed an invalid parameter, etc.
Man Pages
The traditional place to look for information about various
system calls under Unix systems are the man pages.
FreeBSD describes its system calls in section 2, sometimes
in section 3.
For example, open 2 says:
If successful, open() returns a non-negative
integer, termed a file descriptor. It returns -1 on failure,
and sets errno to indicate the error.
The assembly language programmer new to Unix and FreeBSD will
immediately ask the puzzling question: Where is
errno and how do I get to it?
The information presented in the man pages applies
to C programs. The assembly language programmer needs additional
information.
Where Are the Return Values?
Unfortunately, it depends... For most system calls it is
in EAX , but not for all.
A good rule of thumb,
when working with a system call for
the first time, is to look for
the return value in EAX .
If it is not there, you
need further research.
I am aware of one system call that returns the value in
EDX : SYS_fork . All others
I have worked with use EAX .
But I have not worked with them all yet.
If you cannot find the answer here or anywhere else,
study libc source code and see how it
interfaces with the kernel.
Where Is errno ?
Actually, nowhere...
errno is part of the C language, not the
Unix kernel. When accessing kernel services directly, the
error code is returned in EAX ,
the same register the proper
return value generally ends up in.
This makes perfect sense. If there is no error, there is
no error code. If there is an error, there is no return
value. One register can contain either.
Determining an Error Occurred
When using the standard FreeBSD calling convention,
the carry flag is cleared upon success,
set upon failure.
When using the Linux emulation mode, the signed
value in EAX is non-negative upon success,
and contains the return value. In case of an error, the value
is negative, i.e., -errno .
Creating Portable Code
Portability is generally not one of the strengths of assembly language.
Yet, writing assembly language programs for different platforms is
possible, especially with nasm . I have written
assembly language libraries that can be assembled for such different
operating systems as Windows and FreeBSD.
It is all the more possible when you want your code to run
on two platforms which, while different, are based on
similar architectures.
For example, FreeBSD is Unix, Linux is Unix-like. I only
mentioned three differences between them (from an assembly language
programmer's perspective): The calling convention, the
function numbers, and the way of returning values.
Dealing with Function Numbers
In many cases the function numbers are the same. However,
even when they are not, the problem is easy to deal with:
Instead of using numbers in your code, use constants which
you have declared differently depending on the target
architecture:
%ifdef LINUX
%define SYS_execve 11
%else
%define SYS_execve 59
%endif
Dealing with Conventions
Both, the calling convention, and the return value (the
errno problem) can be resolved with macros:
%ifdef LINUX
%macro system 0
call kernel
%endmacro
align 4
kernel:
push ebx
push ecx
push edx
push esi
push edi
push ebp
mov ebx, [esp+32]
mov ecx, [esp+36]
mov edx, [esp+40]
mov esi, [esp+44]
mov ebp, [esp+48]
int 80h
pop ebp
pop edi
pop esi
pop edx
pop ecx
pop ebx
or eax, eax
js .errno
clc
ret
.errno:
neg eax
stc
ret
%else
%macro system 0
int 80h
%endmacro
%endif
Dealing with Other Portability Issues
The above solutions can handle most cases of writing code
portable between FreeBSD and Linux. Nevertheless, with some
kernel services the differences are deeper.
In that case, you need to write two different handlers
for those particular system calls, and use conditional
assembly. Luckily, most of your code does something other
than calling the kernel, so usually you will only need
a few such conditional sections in your code.
Using a Library
You can avoid portability issues in your main code altogether
by writing a library of system calls. Create a separate library
for FreeBSD, a different one for Linux, and yet other libraries
for more operating systems.
In your library, write a separate function (or procedure, if
you prefer the traditional assembly language terminology) for each system
call. Use the C calling convention of passing parameters.
But still use EAX to pass the call number in.
In that case, your FreeBSD library can be very simple, as
many seemingly different functions can be just labels to
the same code:
sys.open:
sys.close:
[etc...]
int 80h
ret
Your Linux library will require more different functions.
But even here you can group system calls using the same
number of parameters:
sys.exit:
sys.close:
[etc... one-parameter functions]
push ebx
mov ebx, [esp+12]
int 80h
pop ebx
jmp sys.return
...
sys.return:
or eax, eax
js sys.err
clc
ret
sys.err:
neg eax
stc
ret
The library approach may seem inconvenient at first because
it requires you to produce a separate file your code depends
on. But it has many advantages: For one, you only need to
write it once and can use it for all your programs. You can
even let other assembly language programmers use it, or perhaps use
one written by someone else. But perhaps the greatest
advantage of the library is that your code can be ported
to other systems, even by other programmers, by simply
writing a new library without any changes to your code.
If you do not like the idea of having a library, you can
at least place all your system calls in a separate assembly language file
and link it with your main program. Here, again, all porters
have to do is create a new object file to link with your
main program.
Using an Include File
If you are releasing your software as (or with)
source code, you can use macros and place them
in a separate file, which you include in your
code.
Porters of your software will simply write a new
include file. No library or external object file
is necessary, yet your code is portable without any
need to edit the code.
This is the approach we will use throughout this chapter.
We will name our include file system.inc , and
add to it whenever we deal with a new system call.
We can start our system.inc by declaring the
standard file descriptors:
%define stdin 0
%define stdout 1
%define stderr 2
Next, we create a symbolic name for each system call:
%define SYS_nosys 0
%define SYS_exit 1
%define SYS_fork 2
%define SYS_read 3
%define SYS_write 4
; [etc...]
We add a short, non-global procedure with a long name,
so we do not accidentally reuse the name in our code:
section .text
align 4
access.the.bsd.kernel:
int 80h
ret
We create a macro which takes one argument, the syscall number:
%macro system 1
mov eax, %1
call access.the.bsd.kernel
%endmacro
Finally, we create macros for each syscall. These macros take
no arguments.
%macro sys.exit 0
system SYS_exit
%endmacro
%macro sys.fork 0
system SYS_fork
%endmacro
%macro sys.read 0
system SYS_read
%endmacro
%macro sys.write 0
system SYS_write
%endmacro
; [etc...]
Go ahead, enter it into your editor and save it as
system.inc . We will add more to it as we
discuss more syscalls.
Our First Program
We are now ready for our first program, the mandatory
Hello, World!
1: %include 'system.inc'
2:
3: section .data
4: hello db 'Hello, World!', 0Ah
5: hbytes equ $-hello
6:
7: section .text
8: global _start
9: _start:
10: push dword hbytes
11: push dword hello
12: push dword stdout
13: sys.write
14:
15: push dword 0
16: sys.exit
Here is what it does: Line 1 includes the defines, the macros,
and the code from system.inc .
Lines 3-5 are the data: Line 3 starts the data section/segment.
Line 4 contains the string "Hello, World!" followed by a new
line (0Ah ). Line 5 creates a constant that contains
the length of the string from line 4 in bytes.
Lines 7-16 contain the code. Note that FreeBSD uses the elf
file format for its executables, which requires every
program to start at the point labeled _start (or, more
precisely, the linker expects that). This label has to be
global.
Lines 10-13 ask the system to write hbytes bytes
of the hello string to stdout .
Lines 15-16 ask the system to end the program with the return
value of 0 . The SYS_exit syscall never
returns, so the code ends there.
If you have come to Unix from MS DOS
assembly language background, you may be used to writing directly
to the video hardware. You will never have to worry about
this in FreeBSD, or any other flavor of Unix. As far as
you are concerned, you are writing to a file known as
stdout . This can be the video screen, or
a telnet terminal, or an actual file,
or even the input of another program. Which one it is,
is for the system to figure out.
Assembling the Code
Type the code (except the line numbers) in an editor, and save
it in a file named hello.asm . You need
nasm to assemble it.
Installing nasm
If you do not have nasm , type:
&prompt.user; su
Password:your root password
&prompt.root; cd /usr/ports/devel/nasm
&prompt.root; make install
&prompt.root; exit
&prompt.user;
You may type make install clean instead of just
make install if you do not want to keep
nasm source code.
Either way, FreeBSD will automatically download
nasm from the Internet,
compile it, and install it on your system.
If your system is not FreeBSD, you need to get
nasm from its
home
page . You can still use it to assemble FreeBSD code.
Now you can assemble, link, and run the code:
&prompt.user; nasm -f elf hello.asm
&prompt.user; ld -s -o hello hello.o
&prompt.user; ./hello
Hello, World!
&prompt.user;
Writing Unix Filters
A common type of Unix application is a filter—a program
that reads data from the stdin , processes it
somehow, then writes the result to stdout .
In this chapter, we shall develop a simple filter, and
learn how to read from stdin and write to
stdout . This filter will convert each byte
of its input into a hexadecimal number followed by a
blank space.
%include 'system.inc'
section .data
hex db '0123456789ABCDEF'
buffer db 0, 0, ' '
section .text
global _start
_start:
; read a byte from stdin
push dword 1
push dword buffer
push dword stdin
sys.read
add esp, byte 12
or eax, eax
je .done
; convert it to hex
movzx eax, byte [buffer]
mov edx, eax
shr dl, 4
mov dl, [hex+edx]
mov [buffer], dl
and al, 0Fh
mov al, [hex+eax]
mov [buffer+1], al
; print it
push dword 3
push dword buffer
push dword stdout
sys.write
add esp, byte 12
jmp short _start
.done:
push dword 0
sys.exit
In the data section we create an array called hex .
It contains the 16 hexadecimal digits in ascending order.
The array is followed by a buffer which we will use for
both input and output. The first two bytes of the buffer
are initially set to 0 . This is where we will write
the two hexadecimal digits (the first byte also is
where we will read the input). The third byte is a
space.
The code section consists of four parts: Reading the byte,
converting it to a hexadecimal number, writing the result,
and eventually exiting the program.
To read the byte, we ask the system to read one byte
from stdin , and store it in the first byte
of the buffer . The system returns the number
of bytes read in EAX . This will be 1
while data is coming, or 0 , when no more input
data is available. Therefore, we check the value of
EAX . If it is 0 ,
we jump to .done , otherwise we continue.
For simplicity sake, we are ignoring the possibility
of an error condition at this time.
The hexadecimal conversion reads the byte from the
buffer into EAX , or actually just
AL , while clearing the remaining bits of
EAX to zeros. We also copy the byte to
EDX because we need to convert the upper
four bits (nibble) separately from the lower
four bits. We store the result in the first two
bytes of the buffer.
Next, we ask the system to write the three bytes
of the buffer, i.e., the two hexadecimal digits and
the blank space, to stdout . We then
jump back to the beginning of the program and
process the next byte.
Once there is no more input left, we ask the system
to exit our program, returning a zero, which is
the traditional value meaning the program was
successful.
Go ahead, and save the code in a file named hex.asm ,
then type the following (the ^D means press the
control key and type D while holding the
control key down):
&prompt.user; nasm -f elf hex.asm
&prompt.user; ld -s -o hex hex.o
&prompt.user; ./hex
Hello, World!
48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A Here I come!
48 65 72 65 20 49 20 63 6F 6D 65 21 0A ^D &prompt.user;
If you are migrating to Unix from MS DOS ,
you may be wondering why each line ends with 0A
instead of 0D 0A .
This is because Unix does not use the cr/lf convention, but
a "new line" convention, which is 0A in hexadecimal.
Can we improve this? Well, for one, it is a bit confusing because
once we have converted a line of text, our input no longer
starts at the begining of the line. We can modify it to print
a new line instead of a space after each 0A :
%include 'system.inc'
section .data
hex db '0123456789ABCDEF'
buffer db 0, 0, ' '
section .text
global _start
_start:
mov cl, ' '
.loop:
; read a byte from stdin
push dword 1
push dword buffer
push dword stdin
sys.read
add esp, byte 12
or eax, eax
je .done
; convert it to hex
movzx eax, byte [buffer]
mov [buffer+2], cl
cmp al, 0Ah
jne .hex
mov [buffer+2], al
.hex:
mov edx, eax
shr dl, 4
mov dl, [hex+edx]
mov [buffer], dl
and al, 0Fh
mov al, [hex+eax]
mov [buffer+1], al
; print it
push dword 3
push dword buffer
push dword stdout
sys.write
add esp, byte 12
jmp short .loop
.done:
push dword 0
sys.exit
We have stored the space in the CL register. We can
do this safely because, unlike Microsoft Windows, Unix system
calls do not modify the value of any register they do not use
to return a value in.
That means we only need to set CL once. We have, therefore,
added a new label .loop and jump to it for the next byte
instead of jumping at _start . We have also added the
.hex label so we can either have a blank space or a
new line as the third byte of the buffer .
Once you have changed hex.asm to reflect
these changes, type:
&prompt.user; nasm -f elf hex.asm
&prompt.user; ld -s -o hex hex.o
&prompt.user; ./hex
Hello, World!
48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A
Here I come!
48 65 72 65 20 49 20 63 6F 6D 65 21 0A
^D &prompt.user;
That looks better. But this code is quite inefficient! We
are making a system call for every single byte twice (once
to read it, another time to write the output).
Buffered Input and Output
We can improve the efficiency of our code by buffering our
input and output. We create an input buffer and read a whole
sequence of bytes at one time. Then we fetch them one by one
from the buffer.
We also create an output buffer. We store our output in it until
it is full. At that time we ask the kernel to write the contents
of the buffer to stdout .
The program ends when there is no more input. But we still need
to ask the kernel to write the contents of our output buffer
to stdout one last time, otherwise some of our output
would make it to the output buffer, but never be sent out.
Do not forget that, or you will be wondering why some of your
output is missing.
%include 'system.inc'
%define BUFSIZE 2048
section .data
hex db '0123456789ABCDEF'
section .bss
ibuffer resb BUFSIZE
obuffer resb BUFSIZE
section .text
global _start
_start:
sub eax, eax
sub ebx, ebx
sub ecx, ecx
mov edi, obuffer
.loop:
; read a byte from stdin
call getchar
; convert it to hex
mov dl, al
shr al, 4
mov al, [hex+eax]
call putchar
mov al, dl
and al, 0Fh
mov al, [hex+eax]
call putchar
mov al, ' '
cmp dl, 0Ah
jne .put
mov al, dl
.put:
call putchar
jmp short .loop
align 4
getchar:
or ebx, ebx
jne .fetch
call read
.fetch:
lodsb
dec ebx
ret
read:
push dword BUFSIZE
mov esi, ibuffer
push esi
push dword stdin
sys.read
add esp, byte 12
mov ebx, eax
or eax, eax
je .done
sub eax, eax
ret
align 4
.done:
call write ; flush output buffer
push dword 0
sys.exit
align 4
putchar:
stosb
inc ecx
cmp ecx, BUFSIZE
je write
ret
align 4
write:
sub edi, ecx ; start of buffer
push ecx
push edi
push dword stdout
sys.write
add esp, byte 12
sub eax, eax
sub ecx, ecx ; buffer is empty now
ret
We now have a third section in the source code, named
.bss . This section is not included in our
executable file, and, therefore, cannot be initialized. We use
resb instead of db .
It simply reserves the requested size of uninitialized memory
for our use.
We take advantage of the fact that the system does not modify the
registers: We use registers for what, otherwise, would have to be
global variables stored in the .data section. This is
also why the Unix convention of passing parameters to system calls
on the stack is superior to the Microsoft convention of passing
them in the registers: We can keep the registers for our own use.
We use EDI and ESI as pointers to the next byte
to be read from or written to. We use EBX and
ECX to keep count of the number of bytes in the
two buffers, so we know when to dump the output to, or read more
input from, the system.
Let us see how it works now:
&prompt.user; nasm -f elf hex.asm
&prompt.user; ld -s -o hex hex.o
&prompt.user; ./hex
Hello, World!
Here I come!
48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A
48 65 72 65 20 49 20 63 6F 6D 65 21 0A
^D &prompt.user;
Not what you expected? The program did not print the output
until we pressed ^D . That is easy to fix by
inserting three lines of code to write the output every time
we have converted a new line to 0A . I have marked
the three lines with > (do not copy the > in your
hex.asm ).
%include 'system.inc'
%define BUFSIZE 2048
section .data
hex db '0123456789ABCDEF'
section .bss
ibuffer resb BUFSIZE
obuffer resb BUFSIZE
section .text
global _start
_start:
sub eax, eax
sub ebx, ebx
sub ecx, ecx
mov edi, obuffer
.loop:
; read a byte from stdin
call getchar
; convert it to hex
mov dl, al
shr al, 4
mov al, [hex+eax]
call putchar
mov al, dl
and al, 0Fh
mov al, [hex+eax]
call putchar
mov al, ' '
cmp dl, 0Ah
jne .put
mov al, dl
.put:
call putchar
> cmp al, 0Ah
> jne .loop
> call write
jmp short .loop
align 4
getchar:
or ebx, ebx
jne .fetch
call read
.fetch:
lodsb
dec ebx
ret
read:
push dword BUFSIZE
mov esi, ibuffer
push esi
push dword stdin
sys.read
add esp, byte 12
mov ebx, eax
or eax, eax
je .done
sub eax, eax
ret
align 4
.done:
call write ; flush output buffer
push dword 0
sys.exit
align 4
putchar:
stosb
inc ecx
cmp ecx, BUFSIZE
je write
ret
align 4
write:
sub edi, ecx ; start of buffer
push ecx
push edi
push dword stdout
sys.write
add esp, byte 12
sub eax, eax
sub ecx, ecx ; buffer is empty now
ret
Now, let us see how it works:
&prompt.user; nasm -f elf hex.asm
&prompt.user; ld -s -o hex hex.o
&prompt.user; ./hex
Hello, World!
48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A
Here I come!
48 65 72 65 20 49 20 63 6F 6D 65 21 0A
^D &prompt.user;
Not bad for a 644-byte executable, is it!
This approach to buffered input/output still
contains a hidden danger. I will discuss—and
fix—it later, when I talk about the
dark
side of buffering.
How to Unread a Character
This may be a somewhat advanced topic, mostly of interest to
programmers familiar with the theory of compilers. If you wish,
you may skip to the next
section, and perhaps read this later.
While our sample program does not require it, more sophisticated
filters often need to look ahead. In other words, they may need
to see what the next character is (or even several characters).
If the next character is of a certain value, it is part of the
token currently being processed. Otherwise, it is not.
For example, you may be parsing the input stream for a textual
string (e.g., when implementing a language compiler): If a
character is followed by another character, or perhaps a digit,
it is part of the token you are processing. If it is followed by
white space, or some other value, then it is not part of the
current token.
This presents an interesting problem: How to return the next
character back to the input stream, so it can be read again
later?
One possible solution is to store it in a character variable,
then set a flag. We can modify getchar to check the flag,
and if it is set, fetch the byte from that variable instead of the
input buffer, and reset the flag. But, of course, that slows us
down.
The C language has an ungetc() function, just for that
purpose. Is there a quick way to implement it in our code?
I would like you to scroll back up and take a look at the
getchar procedure and see if you can find a nice and
fast solution before reading the next paragraph. Then come back
here and see my own solution.
The key to returning a character back to the stream is in how
we are getting the characters to start with:
First we check if the buffer is empty by testing the value
of EBX . If it is zero, we call the
read procedure.
If we do have a character available, we use lodsb , then
decrease the value of EBX . The lodsb
instruction is effectively identical to:
mov al, [esi]
inc esi
The byte we have fetched remains in the buffer until the next
time read is called. We do not know when that happens,
but we do know it will not happen until the next call to
getchar . Hence, to "return" the last-read byte back
to the stream, all we have to do is decrease the value of
ESI and increase the value of EBX :
ungetc:
dec esi
inc ebx
ret
But, be careful! We are perfectly safe doing this if our look-ahead
is at most one character at a time. If we are examining more than
one upcoming character and call ungetc several times
in a row, it will work most of the time, but not all the time
(and will be tough to debug). Why?
Because as long as getchar does not have to call
read , all of the pre-read bytes are still in the buffer,
and our ungetc works without a glitch. But the moment
getchar calls read ,
the contents of the buffer change.
We can always rely on ungetc working properly on the last
character we have read with getchar , but not on anything
we have read before that.
If your program reads more than one byte ahead, you have at least
two choices:
If possible, modify the program so it only reads one byte ahead.
This is the simplest solution.
If that option is not available, first of all determine the maximum
number of characters your program needs to return to the input
stream at one time. Increase that number slightly, just to be
sure, preferably to a multiple of 16—so it aligns nicely.
Then modify the .bss section of your code, and create
a small "spare" buffer right before your input buffer,
something like this:
section .bss
resb 16 ; or whatever the value you came up with
ibuffer resb BUFSIZE
obuffer resb BUFSIZE
You also need to modify your ungetc to pass the value
of the byte to unget in AL :
ungetc:
dec esi
inc ebx
mov [esi], al
ret
With this modification, you can call ungetc
up to 17 times in a row safely (the first call will still
be within the buffer, the remaining 16 may be either within
the buffer or within the "spare").
Command Line Arguments
Our hex program will be more useful if it can
read the names of an input and output file from its command
line, i.e., if it can process the command line arguments.
But... Where are they?
Before a Unix system starts a program, it push es some
data on the stack, then jumps at the _start
label of the program. Yes, I said jumps, not calls. That means the
data can be accessed by reading [esp+offset] ,
or by simply pop ping it.
The value at the top of the stack contains the number of
command line arguments. It is traditionally called
argc , for "argument count."
Command line arguments follow next, all argc of them.
These are typically referred to as argv , for
"argument value(s)." That is, we get argv[0] ,
argv[1] , ... ,
argv[argc-1] . These are not the actual
arguments, but pointers to arguments, i.e., memory addresses of
the actual arguments. The arguments themselves are
NUL-terminated character strings.
The argv list is followed by a NULL pointer,
which is simply a 0 . There is more, but this is
enough for our purposes right now.
If you have come from the MS DOS programming
environment, the main difference is that each argument is in
a separate string. The second difference is that there is no
practical limit on how many arguments there can be.
Armed with this knowledge, we are almost ready for the next
version of hex.asm . First, however, we need to
add a few lines to system.inc :
First, we need to add two new entries to our list of system
call numbers:
%define SYS_open 5
%define SYS_close 6
Then we add two new macros at the end of the file:
%macro sys.open 0
system SYS_open
%endmacro
%macro sys.close 0
system SYS_close
%endmacro
Here, then, is our modified source code:
%include 'system.inc'
%define BUFSIZE 2048
section .data
fd.in dd stdin
fd.out dd stdout
hex db '0123456789ABCDEF'
section .bss
ibuffer resb BUFSIZE
obuffer resb BUFSIZE
section .text
align 4
err:
push dword 1 ; return failure
sys.exit
align 4
global _start
_start:
add esp, byte 8 ; discard argc and argv[0]
pop ecx
jecxz .init ; no more arguments
; ECX contains the path to input file
push dword 0 ; O_RDONLY
push ecx
sys.open
jc err ; open failed
add esp, byte 8
mov [fd.in], eax
pop ecx
jecxz .init ; no more arguments
; ECX contains the path to output file
push dword 420 ; file mode (644 octal)
push dword 0200h | 0400h | 01h
; O_CREAT | O_TRUNC | O_WRONLY
push ecx
sys.open
jc err
add esp, byte 12
mov [fd.out], eax
.init:
sub eax, eax
sub ebx, ebx
sub ecx, ecx
mov edi, obuffer
.loop:
; read a byte from input file or stdin
call getchar
; convert it to hex
mov dl, al
shr al, 4
mov al, [hex+eax]
call putchar
mov al, dl
and al, 0Fh
mov al, [hex+eax]
call putchar
mov al, ' '
cmp dl, 0Ah
jne .put
mov al, dl
.put:
call putchar
cmp al, dl
jne .loop
call write
jmp short .loop
align 4
getchar:
or ebx, ebx
jne .fetch
call read
.fetch:
lodsb
dec ebx
ret
read:
push dword BUFSIZE
mov esi, ibuffer
push esi
push dword [fd.in]
sys.read
add esp, byte 12
mov ebx, eax
or eax, eax
je .done
sub eax, eax
ret
align 4
.done:
call write ; flush output buffer
; close files
push dword [fd.in]
sys.close
push dword [fd.out]
sys.close
; return success
push dword 0
sys.exit
align 4
putchar:
stosb
inc ecx
cmp ecx, BUFSIZE
je write
ret
align 4
write:
sub edi, ecx ; start of buffer
push ecx
push edi
push dword [fd.out]
sys.write
add esp, byte 12
sub eax, eax
sub ecx, ecx ; buffer is empty now
ret
In our .data section we now have two new variables,
fd.in and fd.out . We store the input and
output file descriptors here.
In the .text section we have replaced the references
to stdin and stdout with
[fd.in] and [fd.out] .
The .text section now starts with a simple error
handler, which does nothing but exit the program with a return
value of 1 .
The error handler is before _start so we are
within a short distance from where the errors occur.
Naturally, the program execution still begins at _start .
First, we remove argc and argv[0] from the
stack: They are of no interest to us (in this program, that is).
We pop argv[1] to ECX . This
register is particularly suited for pointers, as we can handle
NULL pointers with jecxz . If argv[1]
is not NULL, we try to open the file named in the first
argument. Otherwise, we continue the program as before: Reading
from stdin , writing to stdout .
If we fail to open the input file (e.g., it does not exist),
we jump to the error handler and quit.
If all went well, we now check for the second argument. If
it is there, we open the output file. Otherwise, we send
the output to stdout . If we fail to open the output
file (e.g., it exists and we do not have the write permission),
we, again, jump to the error handler.
The rest of the code is the same as before, except we close
the input and output files before exiting, and, as mentioned,
we use [fd.in] and [fd.out] .
Our executable is now a whopping 768 bytes long.
Can we still improve it? Of course! Every program can be improved.
Here are a few ideas of what we could do:
Have our error handler print a message to
stderr .
Add error handlers to the read
and write functions.
Close stdin when we open an input file,
stdout when we open an output file.
Add command line switches, such as -i
and -o , so we can list the input and
output files in any order, or perhaps read from
stdin and write to a file.
Print a usage message if command line arguments are incorrect.
I shall leave these enhancements as an exercise to the reader:
You already know everything you need to know to implement them.
Unix Environment
An important Unix concept is the environment, which is defined by
environment variables . Some are set by the system, others
by you, yet others by the shell , or any program
that loads another program.
How to Find Environment Variables
I said earlier that when a program starts executing, the stack
contains argc followed by the NULL-terminated
argv array, followed by something else. The
"something else" is the environment , or,
to be more precise, a NULL-terminated array of pointers to
environment variables . This is often referred
to as env .
The structure of env is the same as that of
argv , a list of memory addresses followed by a
NULL (0 ). In this case, there is no
"envc" —we figure out where the array ends
by searching for the final NULL.
The variables usually come in the name=value
format, but sometimes the =value part
may be missing. We need to account for that possibility.
webvars
I could just show you some code that prints the environment
the same way the Unix env command does. But
I thought it would be more interesting to write a simple
assembly language CGI utility.
CGI: A Quick Overview
I have a
detailed
CGI tutorial on my web site,
but here is a very quick overview of CGI :
The web server communicates with the CGI
program by setting environment variables .
The CGI program
sends its output to stdout .
The web server reads it from there.
It must start with an HTTP
header followed by two blank lines.
It then prints the HTML
code, or whatever other type of data it is producing.
While certain environment variables use
standard names, others vary, depending on the web server. That
makes webvars
quite a useful diagnostic tool.
The Code
Our webvars program, then, must send out
the HTTP header followed by some
HTML mark-up. It then must read
the environment variables one by one
and send them out as part of the
HTML page.
The code follows. I placed comments and explanations
right inside the code:
;;;;;;; webvars.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Copyright (c) 2000 G. Adam Stanislav
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; 1. Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
;
; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
; SUCH DAMAGE.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Version 1.0
;
; Started: 8-Dec-2000
; Updated: 8-Dec-2000
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include 'system.inc'
section .data
http db 'Content-type: text/html', 0Ah, 0Ah
db '<?xml version="1.0" encoding="UTF-8"?>', 0Ah
db '<!DOCTYPE html PUBLIC "-//W3C/DTD XHTML Strict//EN" '
db '"DTD/xhtml1-strict.dtd">', 0Ah
db '<html xmlns="http://www.w3.org/1999/xhtml" '
db 'xml.lang="en" lang="en">', 0Ah
db '<head>', 0Ah
db '<title>Web Environment</title>', 0Ah
db '<meta name="author" content="G. Adam Stanislav" />', 0Ah
db '</head>', 0Ah, 0Ah
db '<body bgcolor="#ffffff" text="#000000" link="#0000ff" '
db 'vlink="#840084" alink="#0000ff">', 0Ah
db '<div class="webvars">', 0Ah
db '<h1>Web Environment</h1>', 0Ah
db '<p>The following <b>environment variables</b> are defined '
db 'on this web server:</p>', 0Ah, 0Ah
db '<table align="center" width="80" border="0" cellpadding="10" '
db 'cellspacing="0" class="webvars">', 0Ah
httplen equ $-http
left db '<tr>', 0Ah
db '<td class="name"><tt>'
leftlen equ $-left
middle db '</tt></td>', 0Ah
db '<td class="value"><tt><b>'
midlen equ $-middle
undef db '<i>(undefined)</i>'
undeflen equ $-undef
right db '</b></tt></td>', 0Ah
db '</tr>', 0Ah
rightlen equ $-right
wrap db '</table>', 0Ah
db '</div>', 0Ah
db '</body>', 0Ah
db '</html>', 0Ah, 0Ah
wraplen equ $-wrap
section .text
global _start
_start:
; First, send out all the http and xhtml stuff that is
; needed before we start showing the environment
push dword httplen
push dword http
push dword stdout
sys.write
; Now find how far on the stack the environment pointers
; are. We have 12 bytes we have pushed before "argc"
mov eax, [esp+12]
; We need to remove the following from the stack:
;
; The 12 bytes we pushed for sys.write
; The 4 bytes of argc
; The EAX*4 bytes of argv
; The 4 bytes of the NULL after argv
;
; Total:
; 20 + eax * 4
;
; Because stack grows down, we need to ADD that many bytes
; to ESP.
lea esp, [esp+20+eax*4]
cld ; This should already be the case, but let's be sure.
; Loop through the environment, printing it out
.loop:
pop edi
or edi, edi ; Done yet?
je near .wrap
; Print the left part of HTML
push dword leftlen
push dword left
push dword stdout
sys.write
; It may be tempting to search for the '=' in the env string next.
; But it is possible there is no '=', so we search for the
; terminating NUL first.
mov esi, edi ; Save start of string
sub ecx, ecx
not ecx ; ECX = FFFFFFFF
sub eax, eax
repne scasb
not ecx ; ECX = string length + 1
mov ebx, ecx ; Save it in EBX
; Now is the time to find '='
mov edi, esi ; Start of string
mov al, '='
repne scasb
not ecx
add ecx, ebx ; Length of name
push ecx
push esi
push dword stdout
sys.write
; Print the middle part of HTML table code
push dword midlen
push dword middle
push dword stdout
sys.write
; Find the length of the value
not ecx
lea ebx, [ebx+ecx-1]
; Print "undefined" if 0
or ebx, ebx
jne .value
mov ebx, undeflen
mov edi, undef
.value:
push ebx
push edi
push dword stdout
sys.write
; Print the right part of the table row
push dword rightlen
push dword right
push dword stdout
sys.write
; Get rid of the 60 bytes we have pushed
add esp, byte 60
; Get the next variable
jmp .loop
.wrap:
; Print the rest of HTML
push dword wraplen
push dword wrap
push dword stdout
sys.write
; Return success
push dword 0
sys.exit
This code produces a 1,396-byte executable. Most of it is data,
i.e., the HTML mark-up we need to send out.
Assemble and link it as usual:
&prompt.user; nasm -f elf webvars.asm
&prompt.user; ld -s -o webvars webvars.o
To use it, you need to upload webvars to your
web server. Depending on how your web server is set up, you
may have to store it in a special cgi-bin directory,
or perhaps rename it with a .cgi extension.
Then you need to use your browser to view its output.
To see its output on my web server, please go to
http://www.int80h.org/webvars/ .
If curious about the additional environment variables
present in a password protected web directory, go to
http://www.int80h.org/private/ ,
using the name asm and password
programmer .
Working with Files
We have already done some basic file work: We know how
to open and close them, how to read and write them using
buffers. But Unix offers much more functionality when it
comes to files. We will examine some of it in this section,
and end up with a nice file conversion utility.
Indeed, let us start at the end, that is, with the file
conversion utility. It always makes programming easier
when we know from the start what the end product is
supposed to do.
One of the first programs I wrote for Unix was
tuc ,
a text-to-Unix file converter. It converts a text
file from other operating systems to a Unix text file.
In other words, it changes from different kind of line endings
to the newline convention of Unix. It saves the output
in a different file. Optionally, it converts a Unix text
file to a DOS text file.
I have used tuc extensively, but always
only to convert from some other OS
to Unix, never the other way. I have always wished
it would just overwrite the file instead of me having
to send the output to a different file. Most of the time,
I end up using it like this:
&prompt.user; tuc myfile tempfile
&prompt.user; mv tempfile myfile
It would be nice to have a ftuc ,
i.e., fast tuc , and use it like this:
&prompt.user; ftuc myfile
In this chapter, then, we will write
ftuc in assembly language
(the original tuc
is in C), and study various
file-oriented kernel services in the process.
At first sight, such a file conversion is very
simple: All you have to do is strip the carriage
returns, right?
If you answered yes, think again: That approach will
work most of the time (at least with MS
DOS text files), but will fail occasionally.
The problem is that not all non-Unix text files end their
line with the carriage return / line feed sequence. Some
use carriage returns without line feeds. Others combine several
blank lines into a single carriage return followed by several
line feeds. And so on.
A text file converter, then, must be able to handle
any possible line endings:
carriage return / line feed
carriage return
line feed / carriage return
line feed
It should also handle files that use some kind of a
combination of the above (e.g., carriage return followed
by several line feeds).
Finite State Machine
The problem is easily solved by the use of a technique
called finite state machine , originally developed
by the designers of digital electronic circuits. A
finite state machine is a digital circuit
whose output is dependent not only on its input but on
its previous input, i.e., on its state. The microprocessor
is an example of a finite state machine : Our
assembly language code is assembled to machine language in which
some assembly language code produces a single byte
of machine language, while others produce several bytes.
As the microprocessor fetches the bytes from the memory
one by one, some of them simply change its state rather than
produce some output. When all the bytes of the op code are
fetched, the microprocessor produces some output, or changes
the value of a register, etc.
Because of that, all software is essentially a sequence of state
instructions for the microprocessor. Nevertheless, the concept
of finite state machine is useful in software design as well.
Our text file converter can be designed as a finite state machine with three
possible states. We could call them states 0-2,
but it will make our life easier if we give them symbolic names:
ordinary
cr
lf
Our program will start in the ordinary
state. During this state, the program action depends on
its input as follows:
If the input is anything other than a carriage return
or line feed, the input is simply passed on to the output. The
state remains unchanged.
If the input is a carriage return, the state is changed
to cr . The input is then discarded, i.e.,
no output is made.
If the input is a line feed, the state is changed to
lf . The input is then discarded.
Whenever we are in the cr state, it is
because the last input was a carriage return, which was
unprocessed. What our software does in this state again
depends on the current input:
If the input is anything other than a carriage return
or line feed, output a line feed, then output the input, then
change the state to ordinary .
If the input is a carriage return, we have received
two (or more) carriage returns in a row. We discard the
input, we output a line feed, and leave the state unchanged.
If the input is a line feed, we output the line feed
and change the state to ordinary . Note that
this is not the same as the first case above – if we tried
to combine them, we would be outputting two line feeds
instead of one.
Finally, we are in the lf state after
we have received a line feed that was not preceded by a
carriage return. This will happen when our file already is
in Unix format, or whenever several lines in a row are
expressed by a single carriage return followed by several
line feeds, or when line ends with a line feed /
carriage return sequence. Here is how we need to handle
our input in this state:
If the input is anything other than a carriage return or
line feed, we output a line feed, then output the input, then
change the state to ordinary . This is exactly
the same action as in the cr state upon
receiving the same kind of input.
If the input is a carriage return, we discard the input,
we output a line feed, then change the state to ordinary .
If the input is a line feed, we output the line feed,
and leave the state unchanged.
The Final State
The above finite state machine works for the entire file, but leaves
the possibility that the final line end will be ignored. That will
happen whenever the file ends with a single carriage return or
a single line feed. I did not think of it when I wrote
tuc , just to discover that
occasionally it strips the last line ending.
This problem is easily fixed by checking the state after the
entire file was processed. If the state is not
ordinary , we simply
need to output one last line feed.
Now that we have expressed our algorithm as a finite state machine ,
we could easily design a dedicated digital electronic
circuit (a "chip") to do the conversion for us. Of course,
doing so would be considerably more expensive than writing
an assembly language program.
The Output Counter
Because our file conversion program may be combining two
characters into one, we need to use an output counter. We
initialize it to 0 , and increase it
every time we send a character to the output. At the end of
the program, the counter will tell us what size we need
to set the file to.
Implementing FSM in Software
The hardest part of working with a finite state machine
is analyzing the problem and expressing it as a
finite state machine . That accomplished,
the software almost writes itself.
In a high-level language, such as C, there are several main
approaches. One is to use a switch statement
which chooses what function should be run. For example,
switch (state) {
default:
case REGULAR:
regular(inputchar);
break;
case CR:
cr(inputchar);
break;
case LF:
lf(inputchar);
break;
}
Another approach is by using an array of function pointers,
something like this:
(output[state])(inputchar);
Yet another is to have state be a
function pointer, set to point at the appropriate function:
(*state)(inputchar);
This is the approach we will use in our program because it is very easy to do in assembly language, and very fast, too. We will simply keep the address of the right procedure in EBX , and then just issue:
call ebx
This is possibly faster than hardcoding the address in the code
because the microprocessor does not have to fetch the address from
the memory—it is already stored in one of its registers. I said
possibly because with the caching modern
microprocessors do, either way may be equally fast.
Memory Mapped Files
Because our program works on a single file, we cannot use the
approach that worked for us before, i.e., to read from an input
file and to write to an output file.
Unix allows us to map a file, or a section of a file,
into memory. To do that, we first need to open the file with the
appropriate read/write flags. Then we use the mmap
system call to map it into the memory. One nice thing about
mmap is that it automatically works with
virtual memory: We can map more of the file into the memory than
we have physical memory available, yet still access it through
regular memory op codes, such as mov ,
lods , and stos .
Whatever changes we make to the memory image of the file will be
written to the file by the system. We do not even have to keep
the file open: As long as it stays mapped, we can
read from it and write to it.
The 32-bit Intel microprocessors can access up to four
gigabytes of memory – physical or virtual. The FreeBSD system
allows us to use up to a half of it for file mapping.
For simplicity sake, in this tutorial we will only convert files
that can be mapped into the memory in their entirety. There are
probably not too many text files that exceed two gigabytes in size.
If our program encounters one, it will simply display a message
suggesting we use the original
tuc instead.
If you examine your copy of syscalls.master ,
you will find two separate syscalls named mmap .
This is because of evolution of Unix: There was the traditional
BSD mmap ,
syscall 71. That one was superceded by the POSIX mmap ,
syscall 197. The FreeBSD system supports both because
older programs were written by using the original BSD
version. But new software uses the POSIX version,
which is what we will use.
The syscalls.master file lists
the POSIX version like this:
197 STD BSD { caddr_t mmap(caddr_t addr, size_t len, int prot, \
int flags, int fd, long pad, off_t pos); }
This differs slightly from what
mmap 2
says. That is because
mmap 2
describes the C version.
The difference is in the long pad argument, which is not present in the C version. However, the FreeBSD syscalls add a 32-bit pad after push ing a 64-bit argument. In this case, off_t is a 64-bit value.
When we are finished working with a memory-mapped file,
we unmap it with the munmap syscall:
For an in-depth treatment of mmap , see
W. Richard Stevens'
Unix
Network Programming, Volume 2, Chapter 12 .
Determining File Size
Because we need to tell mmap how many bytes
of the file to map into the memory, and because we want to map
the entire file, we need to determine the size of the file.
We can use the fstat syscall to get all
the information about an open file that the system can give us.
That includes the file size.
Again, syscalls.master lists two versions
of fstat , a traditional one
(syscall 62), and a POSIX one
(syscall 189). Naturally, we will use the
POSIX version:
189 STD POSIX { int fstat(int fd, struct stat *sb); }
This is a very straightforward call: We pass to it the address
of a stat structure and the descriptor
of an open file. It will fill out the contents of the
stat structure.
I do, however, have to say that I tried to declare the
stat structure in the
.bss section, and
fstat did not like it: It set the carry
flag indicating an error. After I changed the code to allocate
the structure on the stack, everything was working fine.
Changing the File Size
Because our program may combine carriage return / line feed
sequences into straight line feeds, our output may be smaller
than our input. However, since we are placing our output into
the same file we read the input from, we may have to change the
size of the file.
The ftruncate system call allows us to do
just that. Despite its somewhat misleading name, the
ftruncate system call can be used to both
truncate the file (make it smaller) and to grow it.
And yes, we will find two versions of ftruncate
in syscalls.master , an older one
(130), and a newer one (201). We will use
the newer one:
201 STD BSD { int ftruncate(int fd, int pad, off_t length); }
Please note that this one contains a int pad again.
ftuc
We now know everything we need to write ftuc .
We start by adding some new lines in system.inc .
First, we define some constants and structures, somewhere at
or near the beginning of the file:
;;;;;;; open flags
%define O_RDONLY 0
%define O_WRONLY 1
%define O_RDWR 2
;;;;;;; mmap flags
%define PROT_NONE 0
%define PROT_READ 1
%define PROT_WRITE 2
%define PROT_EXEC 4
;;
%define MAP_SHARED 0001h
%define MAP_PRIVATE 0002h
;;;;;;; stat structure
struc stat
st_dev resd 1 ; = 0
st_ino resd 1 ; = 4
st_mode resw 1 ; = 8, size is 16 bits
st_nlink resw 1 ; = 10, ditto
st_uid resd 1 ; = 12
st_gid resd 1 ; = 16
st_rdev resd 1 ; = 20
st_atime resd 1 ; = 24
st_atimensec resd 1 ; = 28
st_mtime resd 1 ; = 32
st_mtimensec resd 1 ; = 36
st_ctime resd 1 ; = 40
st_ctimensec resd 1 ; = 44
st_size resd 2 ; = 48, size is 64 bits
st_blocks resd 2 ; = 56, ditto
st_blksize resd 1 ; = 64
st_flags resd 1 ; = 68
st_gen resd 1 ; = 72
st_lspare resd 1 ; = 76
st_qspare resd 4 ; = 80
endstruc
We define the new syscalls:
%define SYS_mmap 197
%define SYS_munmap 73
%define SYS_fstat 189
%define SYS_ftruncate 201
We add the macros for their use:
%macro sys.mmap 0
system SYS_mmap
%endmacro
%macro sys.munmap 0
system SYS_munmap
%endmacro
%macro sys.ftruncate 0
system SYS_ftruncate
%endmacro
%macro sys.fstat 0
system SYS_fstat
%endmacro
And here is our code:
;;;;;;; Fast Text-to-Unix Conversion (ftuc.asm) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Started: 21-Dec-2000
;; Updated: 22-Dec-2000
;;
;; Copyright 2000 G. Adam Stanislav.
;; All rights reserved.
;;
;;;;;;; v.1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include 'system.inc'
section .data
db 'Copyright 2000 G. Adam Stanislav.', 0Ah
db 'All rights reserved.', 0Ah
usg db 'Usage: ftuc filename', 0Ah
usglen equ $-usg
co db "ftuc: Can't open file.", 0Ah
colen equ $-co
fae db 'ftuc: File access error.', 0Ah
faelen equ $-fae
ftl db 'ftuc: File too long, use regular tuc instead.', 0Ah
ftllen equ $-ftl
mae db 'ftuc: Memory allocation error.', 0Ah
maelen equ $-mae
section .text
align 4
memerr:
push dword maelen
push dword mae
jmp short error
align 4
toolong:
push dword ftllen
push dword ftl
jmp short error
align 4
facerr:
push dword faelen
push dword fae
jmp short error
align 4
cantopen:
push dword colen
push dword co
jmp short error
align 4
usage:
push dword usglen
push dword usg
error:
push dword stderr
sys.write
push dword 1
sys.exit
align 4
global _start
_start:
pop eax ; argc
pop eax ; program name
pop ecx ; file to convert
jecxz usage
pop eax
or eax, eax ; Too many arguments?
jne usage
; Open the file
push dword O_RDWR
push ecx
sys.open
jc cantopen
mov ebp, eax ; Save fd
sub esp, byte stat_size
mov ebx, esp
; Find file size
push ebx
push ebp ; fd
sys.fstat
jc facerr
mov edx, [ebx + st_size + 4]
; File is too long if EDX != 0 ...
or edx, edx
jne near toolong
mov ecx, [ebx + st_size]
; ... or if it is above 2 GB
or ecx, ecx
js near toolong
; Do nothing if the file is 0 bytes in size
jecxz .quit
; Map the entire file in memory
push edx
push edx ; starting at offset 0
push edx ; pad
push ebp ; fd
push dword MAP_SHARED
push dword PROT_READ | PROT_WRITE
push ecx ; entire file size
push edx ; let system decide on the address
sys.mmap
jc near memerr
mov edi, eax
mov esi, eax
push ecx ; for SYS_munmap
push edi
; Use EBX for state machine
mov ebx, ordinary
mov ah, 0Ah
cld
.loop:
lodsb
call ebx
loop .loop
cmp ebx, ordinary
je .filesize
; Output final lf
mov al, ah
stosb
inc edx
.filesize:
; truncate file to new size
push dword 0 ; high dword
push edx ; low dword
push eax ; pad
push ebp
sys.ftruncate
; close it (ebp still pushed)
sys.close
add esp, byte 16
sys.munmap
.quit:
push dword 0
sys.exit
align 4
ordinary:
cmp al, 0Dh
je .cr
cmp al, ah
je .lf
stosb
inc edx
ret
align 4
.cr:
mov ebx, cr
ret
align 4
.lf:
mov ebx, lf
ret
align 4
cr:
cmp al, 0Dh
je .cr
cmp al, ah
je .lf
xchg al, ah
stosb
inc edx
xchg al, ah
; fall through
.lf:
stosb
inc edx
mov ebx, ordinary
ret
align 4
.cr:
mov al, ah
stosb
inc edx
ret
align 4
lf:
cmp al, ah
je .lf
cmp al, 0Dh
je .cr
xchg al, ah
stosb
inc edx
xchg al, ah
stosb
inc edx
mov ebx, ordinary
ret
align 4
.cr:
mov ebx, ordinary
mov al, ah
; fall through
.lf:
stosb
inc edx
ret
Do not use this program on files stored on a disk formated
by MS DOS or Windows. There seems to be a
subtle bug in the FreeBSD code when using mmap
on these drives mounted under FreeBSD: If the file is over
a certain size, mmap will just fill the memory
with zeros, and then copy them to the file overwriting
its contents.
One-Pointed Mind
As a student of Zen, I like the idea of a one-pointed mind:
Do one thing at a time, and do it well.
This, indeed, is very much how Unix works as well. While
a typical Windows application is attempting to do everything
imaginable (and is, therefore, riddled with bugs), a
typical Unix program does only one thing, and it does it
well.
The typical Unix user then essentially assembles his own
applications by writing a shell script which combines the
various existing programs by piping the output of one
program to the input of another.
When writing your own Unix software, it is generally a
good idea to see what parts of the problem you need to
solve can be handled by existing programs, and only
write your own programs for that part of the problem
that you do not have an existing solution for.
CSV
I will illustrate this principle with a specific real-life
example I was faced with recently:
I needed to extract the 11th field of each record from a
database I downloaded from a web site. The database was a
CSV file, i.e., a list of
comma-separated values . That is quite
a standard format for sharing data among people who may be
using different database software.
The first line of the file contains the list of various fields
separated by commas. The rest of the file contains the data
listed line by line, with values separated by commas.
I tried awk , using the comma as a separator.
But because several lines contained a quoted comma,
awk was extracting the wrong field
from those lines.
Therefore, I needed to write my own software to extract the 11th
field from the CSV file. However, going with the Unix
spirit, I only needed to write a simple filter that would do the
following:
Remove the first line from the file;
Change all unquoted commas to a different character;
Remove all quotation marks.
Strictly speaking, I could use sed to remove
the first line from the file, but doing so in my own program
was very easy, so I decided to do it and reduce the size of
the pipeline.
At any rate, writing a program like this took me about
20 minutes. Writing a program that extracts the 11th field
from the CSV file would take a lot longer,
and I could not reuse it to extract some other field from some
other database.
This time I decided to let it do a little more work than
a typical tutorial program would:
It parses its command line for options;
It displays proper usage if it finds wrong arguments;
It produces meaningful error messages.
Here is its usage message:
Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]
All parameters are optional, and can appear in any order.
The -t parameter declares what to replace
the commas with. The tab is the default here.
For example, -t; will replace all unquoted
commas with semicolons.
I did not need the -c option, but it may
come in handy in the future. It lets me declare that I want a
character other than a comma replaced with something else.
For example, -c@ will replace all at signs
(useful if you want to split a list of email addresses
to their user names and domains).
The -p option preserves the first line, i.e.,
it does not delete it. By default, we delete the first
line because in a CSV file it contains the field
names rather than data.
The -i and -o
options let me specify the input and the output files. Defaults
are stdin and stdout ,
so this is a regular Unix filter.
I made sure that both -i filename and
-ifilename are accepted. I also made
sure that only one input and one output files may be
specified.
To get the 11th field of each record, I can now do:
&prompt.user; csv '-t;' data.csv | awk '-F;' '{print $11}'
The code stores the options (except for the file descriptors)
in EDX : The comma in DH , the new
separator in DL , and the flag for
the -p option in the highest bit of
EDX , so a check for its sign will give us a
quick decision what to do.
Here is the code:
;;;;;;; csv.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Convert a comma-separated file to a something-else separated file.
;
; Started: 31-May-2001
; Updated: 1-Jun-2001
;
; Copyright (c) 2001 G. Adam Stanislav
; All rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include 'system.inc'
%define BUFSIZE 2048
section .data
fd.in dd stdin
fd.out dd stdout
usg db 'Usage: csv [-t<delim>] [-c<comma>] [-p] [-o <outfile>] [-i <infile>]', 0Ah
usglen equ $-usg
iemsg db "csv: Can't open input file", 0Ah
iemlen equ $-iemsg
oemsg db "csv: Can't create output file", 0Ah
oemlen equ $-oemsg
section .bss
ibuffer resb BUFSIZE
obuffer resb BUFSIZE
section .text
align 4
ierr:
push dword iemlen
push dword iemsg
push dword stderr
sys.write
push dword 1 ; return failure
sys.exit
align 4
oerr:
push dword oemlen
push dword oemsg
push dword stderr
sys.write
push dword 2
sys.exit
align 4
usage:
push dword usglen
push dword usg
push dword stderr
sys.write
push dword 3
sys.exit
align 4
global _start
_start:
add esp, byte 8 ; discard argc and argv[0]
mov edx, (',' << 8) | 9
.arg:
pop ecx
or ecx, ecx
je near .init ; no more arguments
; ECX contains the pointer to an argument
cmp byte [ecx], '-'
jne usage
inc ecx
mov ax, [ecx]
.o:
cmp al, 'o'
jne .i
; Make sure we are not asked for the output file twice
cmp dword [fd.out], stdout
jne usage
; Find the path to output file - it is either at [ECX+1],
; i.e., -ofile --
; or in the next argument,
; i.e., -o file
inc ecx
or ah, ah
jne .openoutput
pop ecx
jecxz usage
.openoutput:
push dword 420 ; file mode (644 octal)
push dword 0200h | 0400h | 01h
; O_CREAT | O_TRUNC | O_WRONLY
push ecx
sys.open
jc near oerr
add esp, byte 12
mov [fd.out], eax
jmp short .arg
.i:
cmp al, 'i'
jne .p
; Make sure we are not asked twice
cmp dword [fd.in], stdin
jne near usage
; Find the path to the input file
inc ecx
or ah, ah
jne .openinput
pop ecx
or ecx, ecx
je near usage
.openinput:
push dword 0 ; O_RDONLY
push ecx
sys.open
jc near ierr ; open failed
add esp, byte 8
mov [fd.in], eax
jmp .arg
.p:
cmp al, 'p'
jne .t
or ah, ah
jne near usage
or edx, 1 << 31
jmp .arg
.t:
cmp al, 't' ; redefine output delimiter
jne .c
or ah, ah
je near usage
mov dl, ah
jmp .arg
.c:
cmp al, 'c'
jne near usage
or ah, ah
je near usage
mov dh, ah
jmp .arg
align 4
.init:
sub eax, eax
sub ebx, ebx
sub ecx, ecx
mov edi, obuffer
; See if we are to preserve the first line
or edx, edx
js .loop
.firstline:
; get rid of the first line
call getchar
cmp al, 0Ah
jne .firstline
.loop:
; read a byte from stdin
call getchar
; is it a comma (or whatever the user asked for)?
cmp al, dh
jne .quote
; Replace the comma with a tab (or whatever the user wants)
mov al, dl
.put:
call putchar
jmp short .loop
.quote:
cmp al, '"'
jne .put
; Print everything until you get another quote or EOL. If it
; is a quote, skip it. If it is EOL, print it.
.qloop:
call getchar
cmp al, '"'
je .loop
cmp al, 0Ah
je .put
call putchar
jmp short .qloop
align 4
getchar:
or ebx, ebx
jne .fetch
call read
.fetch:
lodsb
dec ebx
ret
read:
jecxz .read
call write
.read:
push dword BUFSIZE
mov esi, ibuffer
push esi
push dword [fd.in]
sys.read
add esp, byte 12
mov ebx, eax
or eax, eax
je .done
sub eax, eax
ret
align 4
.done:
call write ; flush output buffer
; close files
push dword [fd.in]
sys.close
push dword [fd.out]
sys.close
; return success
push dword 0
sys.exit
align 4
putchar:
stosb
inc ecx
cmp ecx, BUFSIZE
je write
ret
align 4
write:
jecxz .ret ; nothing to write
sub edi, ecx ; start of buffer
push ecx
push edi
push dword [fd.out]
sys.write
add esp, byte 12
sub eax, eax
sub ecx, ecx ; buffer is empty now
.ret:
ret
Much of it is taken from hex.asm above. But there
is one important difference: I no longer call write
whenever I am outputing a line feed. Yet, the code can be
used interactively.
I have found a better solution for the interactive problem
since I first started writing this chapter. I wanted to
make sure each line is printed out separately only when needed.
After all, there is no need to flush out every line when used
non-interactively.
The new solution I use now is to call write every
time I find the input buffer empty. That way, when running in
the interactive mode, the program reads one line from the user's
keyboard, processes it, and sees its input buffer is empty. It
flushes its output and reads the next line.
The Dark Side of Buffering
This change prevents a mysterious lockup
in a very specific case. I refer to it as the
dark side of buffering , mostly
because it presents a danger that is not
quite obvious.
It is unlikely to happen with a program like the
csv above, so let us consider yet
another filter: In this case we expect our input
to be raw data representing color values, such as
the red , green , and
blue intensities of a pixel. Our
output will be the negative of our input.
Such a filter would be very simple to write.
Most of it would look just like all the other
filters we have written so far, so I am only
going to show you its inner loop:
.loop:
call getchar
not al ; Create a negative
call putchar
jmp short .loop
Because this filter works with raw data,
it is unlikely to be used interactively.
But it could be called by image manipulation software.
And, unless it calls write before each call
to read , chances are it will lock up.
Here is what might happen:
The image editor will load our filter using the
C function popen() .
It will read the first row of pixels from
a bitmap or pixmap.
It will write the first row of pixels to
the pipe leading to
the fd.in of our filter.
Our filter will read each pixel
from its input, turn it to a negative,
and write it to its output buffer.
Our filter will call getchar
to fetch the next pixel.
getchar will find an empty
input buffer, so it will call
read .
read will call the
SYS_read system call.
The kernel will suspend
our filter until the image editor
sends more data to the pipe.
The image editor will read from the
other pipe, connected to the
fd.out of our filter so it can set the first row of the
output image before
it sends us the second row of the input.
The kernel suspends
the image editor until it receives
some output from our filter, so it
can pass it on to the image editor.
At this point our filter waits for the image
editor to send it more data to process, while
the image editor is waiting for our filter
to send it the result of the processing
of the first row. But the result sits in
our output buffer.
The filter and the image editor will continue
waiting for each other forever (or, at least,
until they are killed). Our software has just
entered a
race condition.
This problem does not exist if our filter flushes
its output buffer before asking the
kernel for more input data.
Using the FPU
Strangely enough, most of assembly language literature does not
even mention the existence of the FPU ,
or floating point unit , let alone discuss
programming it.
Yet, never does assembly language shine more than when
we create highly optimized FPU
code by doing things that can be done only in assembly language.
Organization of the FPU
The FPU consists of 8 80–bit floating–point registers.
These are organized in a stack fashion—you can
push a value on TOS
(top of stack ) and you can
pop it.
That said, the assembly language op codes are not push
and pop because those are already taken.
You can push a value on TOS
by using fld , fild ,
and fbld . Several other op codes
let you push many common
constants —such as pi —on
the TOS .
Similarly, you can pop a value by
using fst , fstp ,
fist , fistp , and
fbstp . Actually, only the op
codes that end with a p will
literally pop the value,
the rest will store it
somewhere else without removing it from
the TOS .
We can transfer the data between the
TOS and the computer memory either as
a 32–bit, 64–bit, or 80–bit real ,
a 16–bit, 32–bit, or 64–bit integer ,
or an 80–bit packed decimal .
The 80–bit packed decimal is
a special case of binary coded
decimal which is very convenient when
converting between the ASCII
representation of data and the internal
data of the FPU . It allows us to use
18 significant digits.
No matter how we represent data in the memory,
the FPU always stores it in the 80–bit
real format in its registers.
Its internal precision is at least 19 decimal
digits, so even if we choose to display results
as ASCII in the full
18–digit precision, we are still showing
correct results.
We can perform mathematical operations on the
TOS : We can calculate its
sine , we can scale it
(i.e., we can multiply or divide it by a power
of 2), we can calculate its base–2
logarithm , and many other things.
We can also multiply or
divide it by, add
it to, or subtract it from,
any of the FPU registers (including
itself).
The official Intel op code for the
TOS is st , and
for the registers
st(0) –st(7) .
st and st(0) , then,
refer to the same register.
For whatever reasons, the original author of
nasm has decided to use
different op codes, namely
st0 –st7 .
In other words, there are no parentheses,
and the TOS is always
st0 , never just st .
The Packed Decimal Format
The packed decimal format
uses 10 bytes (80 bits) of
memory to represent 18 digits. The
number represented there is always an
integer .
You can use it to get decimal places
by multiplying the TOS
by a power of 10 first.
The highest bit of the highest byte
(byte 9) is the sign bit :
If it is set, the number is negative ,
otherwise, it is positive .
The rest of the bits of this byte are unused/ignored.
The remaining 9 bytes store the 18 digits
of the number: 2 digits per byte.
The more significant digit is
stored in the high nibble
(4 bits), the less significant
digit in the low nibble .
That said, you might think that -1234567
would be stored in the memory like this (using
hexadecimal notation):
80 00 00 00 00 00 01 23 45 67
Alas it is not! As with everything else of Intel make,
even the packed decimal is
little–endian .
That means our -1234567
is stored like this:
67 45 23 01 00 00 00 00 00 80
Remember that, or you will be pulling your hair out
in desperation!
The book to read—if you can find it—is Richard Startz'
8087/80287/80387
for the IBM PC & Compatibles .
Though it does seem to take the fact about the
little–endian storage of the packed
decimal for granted. I kid you not about the
desperation of trying to figure out what was wrong
with the filter I show below before
it occurred to me I should try the
little–endian order even for this type of data.
Excursion to Pinhole Photography
To write meaningful software, we must not only
understand our programming tools, but also the
field we are creating software for.
Our next filter will help us whenever we want
to build a pinhole camera ,
so, we need some background in pinhole
photography before we can continue.
The Camera
The easiest way to describe any camera ever built
is as some empty space enclosed in some
lightproof material, with a small hole in the
enclosure.
The enclosure is usually sturdy (e.g., a box),
though sometimes it is flexible (the bellows).
It is quite dark inside the camera. However, the
hole lets light rays in through a single point
(though in some cases there may be several).
These light rays form an image, a representation
of whatever is outside the camera, in front of the
hole.
If some light sensitive material (such as film)
is placed inside the camera, it can capture the
image.
The hole often contains a lens , or
a lens assembly, often called the objective .
The Pinhole
But, strictly speaking, the lens is not necessary:
The original cameras did not use a lens but a
pinhole . Even today, pinholes
are used, both as a tool to study how cameras
work, and to achieve a special kind of image.
The image produced by the pinhole
is all equally sharp. Or blurred .
There is an ideal size for a pinhole: If it is
either larger or smaller, the image loses its
sharpness.
Focal Length
This ideal pinhole diameter is a function
of the square root of focal
length , which is the distance of the
pinhole from the film.
D = PC * sqrt(FL)
In here, D is the
ideal diameter of the pinhole,
FL is the focal length,
and PC is a pinhole
constant. According to Jay Bender,
its value is 0.04 , while
Kenneth Connors has determined it to
be 0.037 . Others have
proposed other values. Plus, this
value is for the daylight only: Other types
of light will require a different constant,
whose value can only be determined by
experimentation.
The F–Number
The f–number is a very useful measure of
how much light reaches the film. A light
meter can determine that, for example,
to expose a film of specific sensitivity
with f5.6 may require the exposure to last
1/1000 sec.
It does not matter whether it is a 35–mm
camera, or a 6x9cm camera, etc.
As long as we know the f–number, we can determine
the proper exposure.
The f–number is easy to calculate:
F = FL / D
In other words, the f–number equals the focal
length divided by the diameter of the pinhole.
It also means a higher f–number either implies
a smaller pinhole or a larger focal distance,
or both. That, in turn, implies, the higher
the f–number, the longer the exposure has to be.
Furthermore, while pinhole diameter and focal
distance are one–dimensional measurements,
both, the film and the pinhole, are two–dimensional.
That means that
if you have measured the exposure at f–number
A as t , then the exposure
at f–number B is:
t * (B / A)²
Normalized F–Number
While many modern cameras can change the diameter
of their pinhole, and thus their f–number, quite
smoothly and gradually, such was not always the case.
To allow for different f–numbers, cameras typically
contained a metal plate with several holes of
different sizes drilled to them.
Their sizes were chosen according to the above
formula in such a way that the resultant f–number
was one of standard f–numbers used on all cameras
everywhere. For example, a very old Kodak Duaflex IV
camera in my possession has three such holes for
f–numbers 8, 11, and 16.
A more recently made camera may offer f–numbers of
2.8, 4, 5.6, 8, 11,
16, 22, and 32 (as well as others).
These numbers were not chosen arbitrarily: They all are
powers of the square root of 2, though they may
be rounded somewhat.
The F–Stop
A typical camera is designed in such a way that setting
any of the normalized f–numbers changes the feel of the
dial. It will naturally stop in that
position. Because of that, these positions of the dial
are called f–stops.
Since the f–numbers at each stop are powers of the
square root of 2, moving the dial by 1
stop will double the amount of light required for
proper exposure. Moving it by 2 stops will
quadruple the required exposure. Moving the dial by
3 stops will require the increase in exposure
8 times, etc.
Designing the Pinhole Software
We are now ready to decide what exactly we want our
pinhole software to do.
Processing Program Input
Since its main purpose is to help us design a working
pinhole camera, we will use the focal
length as the input to the program. This is something
we can determine without software: Proper focal length
is determined by the size of the film and by the need
to shoot "regular" pictures, wide angle pictures, or
telephoto pictures.
Most of the programs we have written so far worked with
individual characters, or bytes, as their input: The
hex program converted individual bytes
into a hexadecimal number, the csv
program either let a character through, or deleted it,
or changed it to a different character, etc.
One program, ftuc used the state machine
to consider at most two input bytes at a time.
But our pinhole program cannot just
work with individual characters, it has to deal with
larger syntactic units.
For example, if we want the program to calculate the
pinhole diameter (and other values we will discuss
later) at the focal lengths of 100 mm ,
150 mm , and 210 mm , we may want
to enter something like this:
100, 150, 210
Our program needs to consider more than a single byte of
input at a time. When it sees the first 1 ,
it must understand it is seeing the first digit of a
decimal number. When it sees the 0 and
the other 0 , it must know it is seeing
more digits of the same number.
When it encounters the first comma, it must know it is
no longer receiving the digits of the first number.
It must be able to convert the digits of the first number
into the value of 100 . And the digits of the
second number into the value of 150 . And,
of course, the digits of the third number into the
numeric value of 210 .
We need to decide what delimiters to accept: Do the
input numbers have to be separated by a comma? If so,
how do we treat two numbers separated by something else?
Personally, I like to keep it simple. Something either
is a number, so I process it. Or it is not a number,
so I discard it. I don't like the computer complaining
about me typing in an extra character when it is
obvious that it is an extra character. Duh!
Plus, it allows me to break up the monotony of computing
and type in a query instead of just a number:
What is the best pinhole diameter for the focal length of 150?
There is no reason for the computer to spit out
a number of complaints:
Syntax error: What
Syntax error: is
Syntax error: the
Syntax error: best
Et cetera, et cetera, et cetera.
Secondly, I like the # character to denote
the start of a comment which extends to the end of the
line. This does not take too much effort to code, and
lets me treat input files for my software as executable
scripts.
In our case, we also need to decide what units the
input should come in: We choose millimeters
because that is how most photographers measure
the focus length.
Finally, we need to decide whether to allow the use
of the decimal point (in which case we must also
consider the fact that much of the world uses a
decimal comma ).
In our case allowing for the decimal point/comma
would offer a false sense of precision: There is
little if any noticeable difference between the
focus lengths of 50 and 51 ,
so allowing the user to input something like
50.5 is not a good idea. This is
my opinion, mind you, but I am the one writing
this program. You can make other choices in yours,
of course.
Offering Options
The most important thing we need to know when building
a pinhole camera is the diameter of the pinhole. Since
we want to shoot sharp images, we will use the above
formula to calculate the pinhole diameter from focal length.
As experts are offering several different values for the
PC constant, we will need to have the choice.
It is traditional in Unix programming to have two main ways
of choosing program parameters, plus to have a default for
the time the user does not make a choice.
Why have two ways of choosing?
One is to allow a (relatively) permanent
choice that applies automatically each time the
software is run without us having to tell it over and
over what we want it to do.
The permanent choices may be stored in a configuration
file, typically found in the user's home directory.
The file usually has the same name as the application
but is started with a dot. Often "rc"
is added to the file name. So, ours could be
~/.pinhole or ~/.pinholerc .
(The ~/ means current user's
home directory.)
The configuration file is used mostly by programs
that have many configurable parameters. Those
that have only one (or a few) often use a different
method: They expect to find the parameter in an
environment variable . In our case,
we might look at an environment variable named
PINHOLE .
Usually, a program uses one or the other of the
above methods. Otherwise, if a configuration
file said one thing, but an environment variable
another, the program might get confused (or just
too complicated).
Because we only need to choose one
such parameter, we will go with the second method
and search the environment for a variable named
PINHOLE .
The other way allows us to make ad hoc
decisions: "Though I usually want
you to use 0.039, this time I want 0.03872."
In other words, it allows us to override
the permanent choice.
This type of choice is usually done with command
line parameters.
Finally, a program always needs a
default . The user may not make
any choices. Perhaps he does not know what
to choose. Perhaps he is "just browsing."
Preferably, the default will be the value
most users would choose anyway. That way
they do not need to choose. Or, rather, they
can choose the default without an additional
effort.
Given this system, the program may find conflicting
options, and handle them this way:
If it finds an ad hoc choice
(e.g., command line parameter), it should
accept that choice. It must ignore any permanent
choice and any default.
Otherwise , if it finds
a permanent option (e.g., an environment
variable), it should accept it, and ignore
the default.
Otherwise , it should use
the default.
We also need to decide what format
our PC option should have.
At first site, it seems obvious to use the
PINHOLE=0.04 format for the
environment variable, and -p0.04
for the command line.
Allowing that is actually a security risk.
The PC constant is a very small
number. Naturally, we will test our software
using various small values of PC .
But what will happen if someone runs the program
choosing a huge value?
It may crash the program because we have not
designed it to handle huge numbers.
Or, we may spend more time on the program so
it can handle huge numbers. We might do that
if we were writing commercial software for
computer illiterate audience.
Or, we might say, "Tough!
The user should know better.""
Or, we just may make it impossible for the user
to enter a huge number. This is the approach we
will take: We will use an implied 0.
prefix.
In other words, if the user wants 0.04 ,
we will expect him to type -p04 ,
or set PINHOLE=04 in his environment.
So, if he says -p9999999 , we will
interpret it as 0.9999999 —still
ridiculous but at least safer.
Secondly, many users will just want to go with either
Bender's constant or Connors' constant.
To make it easier on them, we will interpret
-b as identical to -p04 ,
and -c as identical to -p037 .
The Output
We need to decide what we want our software to
send to the output, and in what format.
Since our input allows for an unspecified number
of focal length entries, it makes sense to use
a traditional database–style output of showing
the result of the calculation for each
focal length on a separate line, while
separating all values on one line by a
tab character.
Optionally, we should also allow the user
to specify the use of the CSV
format we have studied earlier. In this case,
we will print out a line of comma–separated
names describing each field of every line,
then show our results as before, but substituting
a comma for the tab .
We need a command line option for the CSV
format. We cannot use -c because
that already means use Connors' constant .
For some strange reason, many web sites refer to
CSV files as "Excel
spreadsheet" (though the CSV
format predates Excel). We will, therefore, use
the -e switch to inform our software
we want the output in the CSV format.
We will start each line of the output with the
focal length. This may sound repetitious at first,
especially in the interactive mode: The user
types in the focal length, and we are repeating it.
But the user can type several focal lengths on one
line. The input can also come in from a file or
from the output of another program. In that case
the user does not see the input at all.
By the same token, the output can go to a file
which we will want to examine later, or it could
go to the printer, or become the input of another
program.
So, it makes perfect sense to start each line with
the focal length as entered by the user.
No, wait! Not as entered by the user. What if the user
types in something like this:
00000000150
Clearly, we need to strip those leading zeros.
So, we might consider reading the user input as is,
converting it to binary inside the FPU ,
and printing it out from there.
But...
What if the user types something like this:
17459765723452353453534535353530530534563507309676764423
Ha! The packed decimal FPU format
lets us input 18–digit numbers. But the
user has entered more than 18 digits. How
do we handle that?
Well, we could modify our code to read
the first 18 digits, enter it to the FPU ,
then read more, multiply what we already have on the
TOS by 10 raised to the number
of additional digits, then add to it.
Yes, we could do that. But in this
program it would be ridiculous (in a different one it may be just the thing to do): Even the circumference of the Earth expressed in
millimeters only takes 11 digits. Clearly,
we cannot build a camera that large (not yet,
anyway).
So, if the user enters such a huge number, he is
either bored, or testing us, or trying to break
into the system, or playing games—doing
anything but designing a pinhole camera.
What will we do?
We will slap him in the face, in a manner of speaking:
17459765723452353453534535353530530534563507309676764423 ??? ??? ??? ??? ???
To achieve that, we will simply ignore any leading zeros.
Once we find a non–zero digit, we will initialize a
counter to 0 and start taking three steps:
Send the digit to the output.
Append the digit to a buffer we will use later to
produce the packed decimal we can send to the
FPU .
Increase the counter.
Now, while we are taking these three steps,
we also need to watch out for one of two
conditions:
If the counter grows above 18,
we stop appending to the buffer. We
continue reading the digits and sending
them to the output.
If, or rather when ,
the next input character is not
a digit, we are done inputting
for now.
Incidentally, we can simply
discard the non–digit, unless it
is a # , which we must
return to the input stream. It
starts a comment, so we must see it
after we are done producing output
and start looking for more input.
That still leaves one possibility
uncovered: If all the user enters
is a zero (or several zeros), we
will never find a non–zero to
display.
We can determine this has happened
whenever our counter stays at 0 .
In that case we need to send 0
to the output, and perform another
"slap in the face":
0 ??? ??? ??? ??? ???
Once we have displayed the focal
length and determined it is valid
(greater than 0
but not exceeding 18 digits),
we can calculate the pinhole diameter.
It is not by coincidence that pinhole
contains the word pin . Indeed,
many a pinhole literally is a pin
hole , a hole carefully punched with the
tip of a pin.
That is because a typical pinhole is very
small. Our formula gets the result in
millimeters. We will multiply it by 1000 ,
so we can output the result in microns .
At this point we have yet another trap to face:
Too much precision.
Yes, the FPU was designed
for high precision mathematics. But we
are not dealing with high precision
mathematics. We are dealing with physics
(optics, specifically).
Suppose we want to convert a truck into
a pinhole camera (we would not be the
first ones to do that!). Suppose its box is
12
meters long, so we have the focal length
of 12000 . Well, using Bender's constant, it gives us square root of
12000 multiplied by 0.04 ,
which is 4.381780460 millimeters,
or 4381.780460 microns.
Put either way, the result is absurdly precise.
Our truck is not exactly 12000
millimeters long. We did not measure its length
with such a precision, so stating we need a pinhole
with the diameter of 4.381780460
millimeters is, well, deceiving. 4.4
millimeters would do just fine.
I "only" used ten digits in the above example.
Imagine the absurdity of going for all 18!
We need to limit the number of significant
digits of our result. One way of doing it
is by using an integer representing microns.
So, our truck would need a pinhole with the diameter
of 4382 microns. Looking at that number, we still decide that 4400 microns,
or 4.4 millimeters is close enough.
Additionally, we can decide that no matter how
big a result we get, we only want to display four
siginificant digits (or any other number
of them, of course). Alas, the FPU
does not offer rounding to a specific number
of digits (after all, it does not view the
numbers as decimal but as binary).
We, therefore, must devise an algorithm to reduce
the number of significant digits.
Here is mine (I think it is awkward—if
you know a better one, please , let me know):
Initialize a counter to 0 .
While the number is greater than or equal to
10000 , divide it by
10 and increase the counter.
Output the result.
While the counter is greater than 0 ,
output 0 and decrease the counter.
The 10000 is only good if you want
four significant digits. For any other
number of significant digits, replace
10000 with 10
raised to the number of significant digits.
We will, then, output the pinhole diameter
in microns, rounded off to four significant
digits.
At this point, we know the focal
-length and the the pinhole
+length and the pinhole
diameter . That means we have enough
information to also calculate the
f–number .
We will display the f–number, rounded to
four significant digits. Chances are the
f–number will tell us very little. To make
it more meaningful, we can find the nearest
normalized f–number , i.e.,
the nearest power of the square root
of 2.
We do that by multiplying the actual f–number
by itself, which, of course, will give us
its square . We will then calculate
its base–2 logarithm, which is much
easier to do than calculating the
base–square–root–of–2 logarithm!
We will round the result to the nearest integer.
Next, we will raise 2 to the result. Actually,
the FPU gives us a good shortcut
to do that: We can use the fscale
op code to "scale" 1, which is
analogous to shift ing an
integer left. Finally, we calculate the square
root of it all, and we have the nearest
normalized f–number.
If all that sounds overwhelming—or too much
work, perhaps—it may become much clearer
if you see the code. It takes 9 op
codes altogether:
fmul st0, st0
fld1
fld st1
fyl2x
frndint
fld1
fscale
fsqrt
fstp st1
The first line, fmul st0, st0 , squares
the contents of the TOS
(top of the stack, same as st ,
called st0 by nasm ).
The fld1 pushes 1
on the TOS .
The next line, fld st1 , pushes
the square back to the TOS .
At this point the square is both in st
and st(2) (it will become
clear why we leave a second copy on the stack
in a moment). st(1) contains
1 .
Next, fyl2x calculates base–2
logarithm of st multiplied by
st(1) . That is why we placed 1 on st(1) before.
At this point, st contains
the logarithm we have just calculated,
st(1) contains the square
of the actual f–number we saved for later.
frndint rounds the TOS
to the nearest integer. fld1 pushes
a 1 . fscale shifts the
1 we have on the TOS
by the value in st(1) ,
effectively raising 2 to st(1) .
Finally, fsqrt calculates
the square root of the result, i.e.,
the nearest normalized f–number.
We now have the nearest normalized
f–number on the TOS ,
the base–2 logarithm rounded to the
nearest integer in st(1) ,
and the square of the actual f–number
in st(2) . We are saving
the value in st(2) for later.
But we do not need the contents of
st(1) anymore. The last
line, fstp st1 , places the
contents of st to
st(1) , and pops. As a
result, what was st(1)
is now st , what was st(2)
is now st(1) , etc.
The new st contains the
normalized f–number. The new
st(1) contains the square
of the actual f–number we have
stored there for posterity.
At this point, we are ready to output
the normalized f–number. Because it is
normalized, we will not round it off to
four significant digits, but will
send it out in its full precision.
The normalized f-number is useful as long
as it is reasonably small and can be found
on our light meter. Otherwise we need a
different method of determining proper
exposure.
Earlier we have figured out the formula
of calculating proper exposure at an arbitrary
f–number from that measured at a different
f–number.
Every light meter I have ever seen can determine
proper exposure at f5.6. We will, therefore,
calculate an "f5.6 multiplier,"
i.e., by how much we need to multiply the exposure measured
at f5.6 to determine the proper exposure
for our pinhole camera.
From the above formula we know this factor can be
calculated by dividing our f–number (the
actual one, not the normalized one) by
5.6 , and squaring the result.
Mathematically, dividing the square of our
f–number by the square of 5.6
will give us the same result.
Computationally, we do not want to square
two numbers when we can only square one.
So, the first solution seems better at first.
But...
5.6 is a constant .
We do not have to have our FPU
waste precious cycles. We can just tell it
to divide the square of the f–number by
whatever 5.6² equals to.
Or we can divide the f–number by 5.6 ,
and then square the result. The two ways
now seem equal.
But, they are not!
Having studied the principles of photography
above, we remember that the 5.6
is actually square root of 2 raised to
the fifth power. An irrational
number. The square of this number is
exactly 32 .
Not only is 32 an integer,
it is a power of 2. We do not need
to divide the square of the f–number by
32 . We only need to use
fscale to shift it right by
five positions. In the FPU
lingo it means we will fscale it
with st(1) equal to
-5 . That is much
faster than a division.
So, now it has become clear why we have
saved the square of the f–number on the
top of the FPU stack.
The calculation of the f5.6 multiplier
is the easiest calculation of this
entire program! We will output it rounded
to four significant digits.
There is one more useful number we can calculate:
The number of stops our f–number is from f5.6.
This may help us if our f–number is just outside
the range of our light meter, but we have
a shutter which lets us set various speeds,
and this shutter uses stops.
Say, our f–number is 5 stops from
f5.6, and the light meter says
we should use 1/1000 sec.
Then we can set our shutter speed to 1/1000
first, then move the dial by 5 stops.
This calculation is quite easy as well. All
we have to do is to calculate the base-2
logarithm of the f5.6 multiplier
we had just calculated (though we need its
value from before we rounded it off). We then
output the result rounded to the nearest integer.
We do not need to worry about having more than
four significant digits in this one: The result
is most likely to have only one or two digits
anyway.
FPU Optimizations
In assembly language we can optimize the FPU
code in ways impossible in high languages,
including C.
Whenever a C function needs to calculate
a floating–point value, it loads all necessary
variables and constants into FPU
registers. It then does whatever calculation is
required to get the correct result. Good C
compilers can optimize that part of the code really
well.
It "returns" the value by leaving
the result on the TOS .
However, before it returns, it cleans up.
Any variables and constants it used in its
calculation are now gone from the FPU .
It cannot do what we just did above: We calculated
the square of the f–number and kept it on the
stack for later use by another function.
We knew we would need that value
later on. We also knew we had enough room on the
stack (which only has room for 8 numbers)
to store it there.
A C compiler has no way of knowing
that a value it has on the stack will be
required again in the very near future.
Of course, the C programmer may know it.
But the only recourse he has is to store the
value in a memory variable.
That means, for one, the value will be changed
from the 80-bit precision used internally
by the FPU to a C double
(64 bits) or even single (32
bits).
That also means that the value must be moved
from the TOS into the memory,
and then back again. Alas, of all FPU
operations, the ones that access the computer
memory are the slowest.
So, whenever programming the FPU
in assembly language, look for the ways of keeping
intermediate results on the FPU
stack.
We can take that idea even further! In our
program we are using a constant
(the one we named PC ).
It does not matter how many pinhole diameters
we are calculating: 1, 10, 20,
1000, we are always using the same constant.
Therefore, we can optimize our program by keeping
the constant on the stack all the time.
Early on in our program, we are calculating the
value of the above constant. We need to divide
our input by 10 for every digit in the
constant.
It is much faster to multiply than to divide.
So, at the start of our program, we divide 10
into 1 to obtain 0.1 , which we
then keep on the stack: Instead of dividing the
input by 10 for every digit,
we multiply it by 0.1 .
By the way, we do not input 0.1 directly,
even though we could. We have a reason for that:
While 0.1 can be expressed with just one
decimal place, we do not know how many binary
places it takes. We, therefore, let the FPU
calculate its binary value to its own high precision.
We are using other constants: We multiply the pinhole
diameter by 1000 to convert it from
millimeters to microns. We compare numbers to
10000 when we are rounding them off to
four significant digits. So, we keep both, 1000
and 10000 , on the stack. And, of course,
we reuse the 0.1 when rounding off numbers
to four digits.
Last but not least, we keep -5 on the stack.
We need it to scale the square of the f–number,
instead of dividing it by 32 . It is not
by coincidence we load this constant last. That makes
it the top of the stack when only the constants
are on it. So, when the square of the f–number is
being scaled, the -5 is at st(1) ,
precisely where fscale expects it to be.
It is common to create certain constants from
scratch instead of loading them from the memory.
That is what we are doing with -5 :
fld1 ; TOS = 1
fadd st0, st0 ; TOS = 2
fadd st0, st0 ; TOS = 4
fld1 ; TOS = 1
faddp st1, st0 ; TOS = 5
fchs ; TOS = -5
We can generalize all these optimizations into one rule:
Keep repeat values on the stack!
PostScript is a stack–oriented
programming language. There are many more books
available about PostScript than about the
FPU assembly language: Mastering
PostScript will help you master the FPU .
pinhole —The Code
;;;;;;; pinhole.asm ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Find various parameters of a pinhole camera construction and use
;
; Started: 9-Jun-2001
; Updated: 10-Jun-2001
;
; Copyright (c) 2001 G. Adam Stanislav
; All rights reserved.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%include 'system.inc'
%define BUFSIZE 2048
section .data
align 4
ten dd 10
thousand dd 1000
tthou dd 10000
fd.in dd stdin
fd.out dd stdout
envar db 'PINHOLE=' ; Exactly 8 bytes, or 2 dwords long
pinhole db '04,', ; Bender's constant (0.04)
connors db '037', 0Ah ; Connors' constant
usg db 'Usage: pinhole [-b] [-c] [-e] [-p <value>] [-o <outfile>] [-i <infile>]', 0Ah
usglen equ $-usg
iemsg db "pinhole: Can't open input file", 0Ah
iemlen equ $-iemsg
oemsg db "pinhole: Can't create output file", 0Ah
oemlen equ $-oemsg
pinmsg db "pinhole: The PINHOLE constant must not be 0", 0Ah
pinlen equ $-pinmsg
toobig db "pinhole: The PINHOLE constant may not exceed 18 decimal places", 0Ah
biglen equ $-toobig
huhmsg db 9, '???'
separ db 9, '???'
sep2 db 9, '???'
sep3 db 9, '???'
sep4 db 9, '???', 0Ah
huhlen equ $-huhmsg
header db 'focal length in millimeters,pinhole diameter in microns,'
db 'F-number,normalized F-number,F-5.6 multiplier,stops '
db 'from F-5.6', 0Ah
headlen equ $-header
section .bss
ibuffer resb BUFSIZE
obuffer resb BUFSIZE
dbuffer resb 20 ; decimal input buffer
bbuffer resb 10 ; BCD buffer
section .text
align 4
huh:
call write
push dword huhlen
push dword huhmsg
push dword [fd.out]
sys.write
add esp, byte 12
ret
align 4
perr:
push dword pinlen
push dword pinmsg
push dword stderr
sys.write
push dword 4 ; return failure
sys.exit
align 4
consttoobig:
push dword biglen
push dword toobig
push dword stderr
sys.write
push dword 5 ; return failure
sys.exit
align 4
ierr:
push dword iemlen
push dword iemsg
push dword stderr
sys.write
push dword 1 ; return failure
sys.exit
align 4
oerr:
push dword oemlen
push dword oemsg
push dword stderr
sys.write
push dword 2
sys.exit
align 4
usage:
push dword usglen
push dword usg
push dword stderr
sys.write
push dword 3
sys.exit
align 4
global _start
_start:
add esp, byte 8 ; discard argc and argv[0]
sub esi, esi
.arg:
pop ecx
or ecx, ecx
je near .getenv ; no more arguments
; ECX contains the pointer to an argument
cmp byte [ecx], '-'
jne usage
inc ecx
mov ax, [ecx]
inc ecx
.o:
cmp al, 'o'
jne .i
; Make sure we are not asked for the output file twice
cmp dword [fd.out], stdout
jne usage
; Find the path to output file - it is either at [ECX+1],
; i.e., -ofile --
; or in the next argument,
; i.e., -o file
or ah, ah
jne .openoutput
pop ecx
jecxz usage
.openoutput:
push dword 420 ; file mode (644 octal)
push dword 0200h | 0400h | 01h
; O_CREAT | O_TRUNC | O_WRONLY
push ecx
sys.open
jc near oerr
add esp, byte 12
mov [fd.out], eax
jmp short .arg
.i:
cmp al, 'i'
jne .p
; Make sure we are not asked twice
cmp dword [fd.in], stdin
jne near usage
; Find the path to the input file
or ah, ah
jne .openinput
pop ecx
or ecx, ecx
je near usage
.openinput:
push dword 0 ; O_RDONLY
push ecx
sys.open
jc near ierr ; open failed
add esp, byte 8
mov [fd.in], eax
jmp .arg
.p:
cmp al, 'p'
jne .c
or ah, ah
jne .pcheck
pop ecx
or ecx, ecx
je near usage
mov ah, [ecx]
.pcheck:
cmp ah, '0'
jl near usage
cmp ah, '9'
ja near usage
mov esi, ecx
jmp .arg
.c:
cmp al, 'c'
jne .b
or ah, ah
jne near usage
mov esi, connors
jmp .arg
.b:
cmp al, 'b'
jne .e
or ah, ah
jne near usage
mov esi, pinhole
jmp .arg
.e:
cmp al, 'e'
jne near usage
or ah, ah
jne near usage
mov al, ','
mov [huhmsg], al
mov [separ], al
mov [sep2], al
mov [sep3], al
mov [sep4], al
jmp .arg
align 4
.getenv:
; If ESI = 0, we did not have a -p argument,
; and need to check the environment for "PINHOLE="
or esi, esi
jne .init
sub ecx, ecx
.nextenv:
pop esi
or esi, esi
je .default ; no PINHOLE envar found
; check if this envar starts with 'PINHOLE='
mov edi, envar
mov cl, 2 ; 'PINHOLE=' is 2 dwords long
rep cmpsd
jne .nextenv
; Check if it is followed by a digit
mov al, [esi]
cmp al, '0'
jl .default
cmp al, '9'
jbe .init
; fall through
align 4
.default:
; We got here because we had no -p argument,
; and did not find the PINHOLE envar.
mov esi, pinhole
; fall through
align 4
.init:
sub eax, eax
sub ebx, ebx
sub ecx, ecx
sub edx, edx
mov edi, dbuffer+1
mov byte [dbuffer], '0'
; Convert the pinhole constant to real
.constloop:
lodsb
cmp al, '9'
ja .setconst
cmp al, '0'
je .processconst
jb .setconst
inc dl
.processconst:
inc cl
cmp cl, 18
ja near consttoobig
stosb
jmp short .constloop
align 4
.setconst:
or dl, dl
je near perr
finit
fild dword [tthou]
fld1
fild dword [ten]
fdivp st1, st0
fild dword [thousand]
mov edi, obuffer
mov ebp, ecx
call bcdload
.constdiv:
fmul st0, st2
loop .constdiv
fld1
fadd st0, st0
fadd st0, st0
fld1
faddp st1, st0
fchs
; If we are creating a CSV file,
; print header
cmp byte [separ], ','
jne .bigloop
push dword headlen
push dword header
push dword [fd.out]
sys.write
.bigloop:
call getchar
jc near done
; Skip to the end of the line if you got '#'
cmp al, '#'
jne .num
call skiptoeol
jmp short .bigloop
.num:
; See if you got a number
cmp al, '0'
jl .bigloop
cmp al, '9'
ja .bigloop
; Yes, we have a number
sub ebp, ebp
sub edx, edx
.number:
cmp al, '0'
je .number0
mov dl, 1
.number0:
or dl, dl ; Skip leading 0's
je .nextnumber
push eax
call putchar
pop eax
inc ebp
cmp ebp, 19
jae .nextnumber
mov [dbuffer+ebp], al
.nextnumber:
call getchar
jc .work
cmp al, '#'
je .ungetc
cmp al, '0'
jl .work
cmp al, '9'
ja .work
jmp short .number
.ungetc:
dec esi
inc ebx
.work:
; Now, do all the work
or dl, dl
je near .work0
cmp ebp, 19
jae near .toobig
call bcdload
; Calculate pinhole diameter
fld st0 ; save it
fsqrt
fmul st0, st3
fld st0
fmul st5
sub ebp, ebp
; Round off to 4 significant digits
.diameter:
fcom st0, st7
fstsw ax
sahf
jb .printdiameter
fmul st0, st6
inc ebp
jmp short .diameter
.printdiameter:
call printnumber ; pinhole diameter
; Calculate F-number
fdivp st1, st0
fld st0
sub ebp, ebp
.fnumber:
fcom st0, st6
fstsw ax
sahf
jb .printfnumber
fmul st0, st5
inc ebp
jmp short .fnumber
.printfnumber:
call printnumber ; F number
; Calculate normalized F-number
fmul st0, st0
fld1
fld st1
fyl2x
frndint
fld1
fscale
fsqrt
fstp st1
sub ebp, ebp
call printnumber
; Calculate time multiplier from F-5.6
fscale
fld st0
; Round off to 4 significant digits
.fmul:
fcom st0, st6
fstsw ax
sahf
jb .printfmul
inc ebp
fmul st0, st5
jmp short .fmul
.printfmul:
call printnumber ; F multiplier
; Calculate F-stops from 5.6
fld1
fxch st1
fyl2x
sub ebp, ebp
call printnumber
mov al, 0Ah
call putchar
jmp .bigloop
.work0:
mov al, '0'
call putchar
align 4
.toobig:
call huh
jmp .bigloop
align 4
done:
call write ; flush output buffer
; close files
push dword [fd.in]
sys.close
push dword [fd.out]
sys.close
finit
; return success
push dword 0
sys.exit
align 4
skiptoeol:
; Keep reading until you come to cr, lf, or eof
call getchar
jc done
cmp al, 0Ah
jne .cr
ret
.cr:
cmp al, 0Dh
jne skiptoeol
ret
align 4
getchar:
or ebx, ebx
jne .fetch
call read
.fetch:
lodsb
dec ebx
clc
ret
read:
jecxz .read
call write
.read:
push dword BUFSIZE
mov esi, ibuffer
push esi
push dword [fd.in]
sys.read
add esp, byte 12
mov ebx, eax
or eax, eax
je .empty
sub eax, eax
ret
align 4
.empty:
add esp, byte 4
stc
ret
align 4
putchar:
stosb
inc ecx
cmp ecx, BUFSIZE
je write
ret
align 4
write:
jecxz .ret ; nothing to write
sub edi, ecx ; start of buffer
push ecx
push edi
push dword [fd.out]
sys.write
add esp, byte 12
sub eax, eax
sub ecx, ecx ; buffer is empty now
.ret:
ret
align 4
bcdload:
; EBP contains the number of chars in dbuffer
push ecx
push esi
push edi
lea ecx, [ebp+1]
lea esi, [dbuffer+ebp-1]
shr ecx, 1
std
mov edi, bbuffer
sub eax, eax
mov [edi], eax
mov [edi+4], eax
mov [edi+2], ax
.loop:
lodsw
sub ax, 3030h
shl al, 4
or al, ah
mov [edi], al
inc edi
loop .loop
fbld [bbuffer]
cld
pop edi
pop esi
pop ecx
sub eax, eax
ret
align 4
printnumber:
push ebp
mov al, [separ]
call putchar
; Print the integer at the TOS
mov ebp, bbuffer+9
fbstp [bbuffer]
; Check the sign
mov al, [ebp]
dec ebp
or al, al
jns .leading
; We got a negative number (should never happen)
mov al, '-'
call putchar
.leading:
; Skip leading zeros
mov al, [ebp]
dec ebp
or al, al
jne .first
cmp ebp, bbuffer
jae .leading
; We are here because the result was 0.
; Print '0' and return
mov al, '0'
jmp putchar
.first:
; We have found the first non-zero.
; But it is still packed
test al, 0F0h
jz .second
push eax
shr al, 4
add al, '0'
call putchar
pop eax
and al, 0Fh
.second:
add al, '0'
call putchar
.next:
cmp ebp, bbuffer
jb .done
mov al, [ebp]
push eax
shr al, 4
add al, '0'
call putchar
pop eax
and al, 0Fh
add al, '0'
call putchar
dec ebp
jmp short .next
.done:
pop ebp
or ebp, ebp
je .ret
.zeros:
mov al, '0'
call putchar
dec ebp
jne .zeros
.ret:
ret
The code follows the same format as all the other
filters we have seen before, with one subtle
exception:
We are no longer assuming that the end of input
implies the end of things to do, something we
took for granted in the character–oriented
filters.
This filter does not process characters. It
processes a language
(albeit a very simple
one, consisting only of numbers).
When we have no more input, it can mean one
of two things:
We are done and can quit. This is the
same as before.
The last character we have read was a digit.
We have stored it at the end of our
ASCII –to–float conversion
buffer. We now need to convert
the contents of that buffer into a
number and write the last line of our
output.
For that reason, we have modified our getchar
and our read routines to return with
the carry flag clear whenever we are
fetching another character from the input, or the
carry flag set whenever there is no more
input.
Of course, we are still using assembly language magic
to do that! Take a good look at getchar .
It always returns with the
carry flag clear .
Yet, our main code relies on the carry
flag to tell it when to quit—and it works.
The magic is in read . Whenever it
receives more input from the system, it just
returns to getchar , which
fetches a character from the input buffer,
clears the carry flag
and returns.
But when read receives no more
input from the system, it does not
return to getchar at all.
Instead, the add esp, byte 4
op code adds 4 to ESP ,
sets the carry
flag , and returns.
So, where does it return to? Whenever a
program uses the call op code,
the microprocessor push es the
return address, i.e., it stores it on
the top of the stack (not the FPU
stack, the system stack, which is in the memory).
When a program uses the ret
op code, the microprocessor pop s
the return value from the stack, and jumps
to the address that was stored there.
But since we added 4 to
ESP (which is the stack
pointer register), we have effectively
given the microprocessor a minor case
of amnesia : It no longer
remembers it was getchar
that call ed read .
And since getchar never
push ed anything before
call ing read ,
the top of the stack now contains the
return address to whatever or whoever
call ed getchar .
As far as that caller is concerned,
he call ed getchar ,
which ret urned with the
carry flag set!
Other than that, the bcdload
routine is caught up in the middle of a
Lilliputian conflict between the Big–Endians
and the Little–Endians.
It is converting the text representation
of a number into that number: The text
is stored in the big–endian order, but
the packed decimal is little–endian.
To solve the conflict, we use the std
op code early on. We cancel it with cld
later on: It is quite important we do not
call anything that may depend on
the default setting of the direction
flag while std is active.
Everything else in this code should be quite
clear, providing you have read the entire chapter
that precedes it.
It is a classical example of the adage that
programming requires a lot of thought and only
a little coding. Once we have thought through every
tiny detail, the code almost writes itself.
Using pinhole
Because we have decided to make the program
ignore any input except for numbers
(and even those inside a comment), we can
actually perform textual queries .
We do not have to , but we can .
In my humble opinion, forming a textual query,
instead of having to follow a very strict
syntax, makes software much more user friendly.
Suppose we want to build a pinhole camera to use the
4x5 inch film. The standard focal
length for that film is about 150mm. We want
to fine–tune our focal length so the
pinhole diameter is as round a number as possible.
Let us also suppose we are quite comfortable with
cameras but somewhat intimidated by computers.
Rather than just have to type in a bunch of numbers,
we want to ask a couple of questions.
Our session might look like this:
&prompt.user; pinhole
Computer,
What size pinhole do I need for the focal length of 150?
150 490 306 362 2930 12
Hmmm... How about 160?
160 506 316 362 3125 12
Let's make it 155, please.
155 498 311 362 3027 12
Ah, let's try 157...
157 501 313 362 3066 12
156?
156 500 312 362 3047 12
That's it! Perfect! Thank you very much!
^D
We have found that while for the focal length
of 150, our pinhole diameter should be 490
microns, or 0.49 mm, if we go with the almost
identical focal length of 156 mm, we can
get away with a pinhole diameter of exactly
one half of a millimeter.
Scripting
Because we have chosen the #
character to denote the start of a comment,
we can treat our pinhole
software as a scripting language .
You have probably seen shell
scripts that start with:
#! /bin/sh
...or...
#!/bin/sh
...because the blank space after the #!
is optional.
Whenever Unix is asked to run an executable
file which starts with the #! ,
it assumes the file is a script. It adds the
command to the rest of the first line of the
script, and tries to execute that.
Suppose now that we have installed pinhole
in /usr/local/bin/ , we can now
write a script to calculate various pinhole
diameters suitable for various focal lengths
commonly used with the 120 film.
The script might look something like this:
#! /usr/local/bin/pinhole -b -i
# Find the best pinhole diameter
# for the 120 film
### Standard
80
### Wide angle
30, 40, 50, 60, 70
### Telephoto
100, 120, 140
Because 120 is a medium size film,
we may name this file medium .
We can set its permissions to execute,
and run it as if it were a program:
&prompt.user; chmod 755 medium
&prompt.user; ./medium
Unix will interpret that last command as:
&prompt.user; /usr/local/bin/pinhole -b -i ./medium
It will run that command and display:
80 358 224 256 1562 11
30 219 137 128 586 9
40 253 158 181 781 10
50 283 177 181 977 10
60 310 194 181 1172 10
70 335 209 181 1367 10
100 400 250 256 1953 11
120 438 274 256 2344 11
140 473 296 256 2734 11
Now, let us enter:
&prompt.user; ./medium -c
Unix will treat that as:
&prompt.user; /usr/local/bin/pinhole -b -i ./medium -c
That gives it two conflicting options:
-b and -c
(Use Bender's constant and use Connors'
constant). We have programmed it so
later options override early ones—our
program will calculate everything
using Connors' constant:
80 331 242 256 1826 11
30 203 148 128 685 9
40 234 171 181 913 10
50 262 191 181 1141 10
60 287 209 181 1370 10
70 310 226 256 1598 11
100 370 270 256 2283 11
120 405 296 256 2739 11
140 438 320 362 3196 12
We decide we want to go with Bender's
constant after all. We want to save its
values as a comma–separated file:
&prompt.user; ./medium -b -e > bender
&prompt.user; cat bender
focal length in millimeters,pinhole diameter in microns,F-number,normalized F-number,F-5.6 multiplier,stops from F-5.6
80,358,224,256,1562,11
30,219,137,128,586,9
40,253,158,181,781,10
50,283,177,181,977,10
60,310,194,181,1172,10
70,335,209,181,1367,10
100,400,250,256,1953,11
120,438,274,256,2344,11
140,473,296,256,2734,11
&prompt.user;
Caveats
Assembly language programmers who "grew up" under
MS DOS and Windows often tend to take shortcuts.
Reading the keyboard scan codes and writing directly to video
memory are two classical examples of practices which, under
MS DOS are not frowned upon but considered the
right thing to do.
The reason? Both the PC BIOS and
MS DOS are notoriously
slow when performing these operations.
You may be tempted to continue similar practices in the
Unix environment. For example, I have seen a web site which
explains how to access the keyboard scan codes on a popular Unix clone.
That is generally a very bad idea
in Unix environment! Let me explain why.
Unix Is Protected
For one thing, it may simply not be possible. Unix runs in
protected mode. Only the kernel and device drivers are allowed
to access hardware directly. Perhaps a particular Unix clone
will let you read the keyboard scan codes, but chances are a real
Unix operating system will not. And even if one version may let you
do it, the next one may not, so your carefully crafted software may
become a dinosaur overnight.
Unix Is an Abstraction
But there is a much more important reason not to try
accessing the hardware directly (unless, of course,
you are writing a device driver), even on the Unix-like
systems that let you do it:
Unix is an abstraction!
There is a major difference in the philosophy of design
between MS DOS and Unix.
MS DOS was designed as a single-user
system. It is run on a computer with a keyboard and a video
screen attached directly to that computer. User input is almost
guaranteed to come from that keyboard. Your program's output
virtually always ends up on that screen.
This is NEVER guaranteed under Unix. It is quite common
for a Unix user to pipe and redirect program input and output:
&prompt.user; program1 | program2 | program3 > file1
If you have written program2 , your input
does not come from the keyboard but from the output of
program1 . Similarly, your output does not
go to the screen but becomes the input for
program3 whose output, in turn,
goes to file1 .
But there is more! Even if you made sure that your input comes
from, and your output goes to, the terminal, there is no guarantee
the terminal is a PC: It may not have its video memory
where you expect it, nor may its keyboard be producing
PC -style scan codes. It may be a Macintosh,
or any other computer.
Now you may be shaking your head: My software is in
PC assembly language, how can
it run on a Macintosh? But I did not say your software
would be running on a Macintosh, only that its terminal
may be a Macintosh.
Under Unix, the terminal does not have to be directly
attached to the computer that runs your software, it can
even be on another continent, or, for that matter, on another
planet. It is perfectly possible that a Macintosh user in
Australia connects to a Unix system in North America (or
anywhere else) via telnet . The
software then runs on one computer, while the terminal is
on a different computer: If you try to read the scan codes,
you will get the wrong input!
Same holds true about any other hardware: A file you are reading
may be on a disk you have no direct access to. A camera you are
reading images from may be on a space shuttle, connected to you
via satellites.
That is why under Unix you must never make any assumptions about
where your data is coming from and going to. Always let the
system handle the physical access to the hardware.
These are caveats, not absolute rules. Exceptions are possible.
For example, if a text editor has determined it is running
on a local machine, it may want to read the scan codes
directly for improved control. I am not mentioning these caveats
to tell you what to do or what not to do, just to make you aware
of certain pitfalls that await you if you have just arrived to Unix
form MS DOS . Of course, creative people often break
rules, and it is OK as long as they know they are breaking
them and why.
Acknowledgements
This tutorial would never have been possible without the
help of many experienced FreeBSD programmers from the
FreeBSD
hackers mailing list, many of whom have patiently
answered my questions, and pointed me in the right direction
in my attempts to explore the inner workings of Unix
system programming in general and FreeBSD in particular.
Thomas M. Sommers opened the door for me. His
How
do I write "Hello, world" in FreeBSD assembler?
web page was my first encounter with an example of
assembly language programming under FreeBSD.
Jake Burkholder has kept the door open by willingly
answering all of my questions and supplying me with
example assembly language source code.
Copyright © 2000-2001 G. Adam Stanislav. All rights reserved.
diff --git a/en_US.ISO8859-1/books/fdp-primer/examples/appendix.sgml b/en_US.ISO8859-1/books/fdp-primer/examples/appendix.sgml
index e093732098..0f6c30f197 100644
--- a/en_US.ISO8859-1/books/fdp-primer/examples/appendix.sgml
+++ b/en_US.ISO8859-1/books/fdp-primer/examples/appendix.sgml
@@ -1,355 +1,355 @@
Examples
This appendix contains example SGML files and command lines you can
use to convert them from one output format to another. If you have
successfully installed the Documentation Project tools then you should
- be able to to use these examples directly.
+ be able to use these examples directly.
These examples are not exhaustive—they do not contain all the
elements you might want to use, particularly in your document's front
matter. For more examples of DocBook markup you should examine the SGML
source for this and other documents, available in the
CVSup doc collection, or
available online starting at http://www.FreeBSD.org/cgi/cvsweb.cgi/doc/ .
To avoid confusion, these examples use the standard DocBook 3.1 DTD
rather than the FreeBSD extension. They also use the stock stylesheets
distributed by Norm Walsh, rather than any customisations made to those
stylesheets by the FreeBSD Documentation Project. This makes them more
useful as generic DocBook examples.
DocBook book
DocBook book
An Example Book
Your first name
Your surname
foo@example.com
2000
Copyright string here
If your book has an abstract then it should go here.
Preface
Your book may have a preface, in which case it should be placed
here.
My first chapter
This is the first chapter in my book.
My first section
This is the first section in my book.
]]>
DocBook article
DocBook article
An example article
Your first name
Your surname
foo@example.com
2000
Copyright string here
If your article has an abstract then it should go here.
My first section
This is the first section in my article.
My first sub-section
This is the first sub-section in my article.
]]>
Producing formatted output
This section assumes that you have installed the software listed in
the textproc/docproj port, either by hand, or by
using the port. Further, it is assumed that your software is installed
in subdirectories under /usr/local/ , and the
directory where binaries have been installed is in your
PATH . Adjust the paths as necessary for your
system.
Using Jade
Converting DocBook to HTML (one large file)
&prompt.user; jade -V nochunks \
-c /usr/local/share/sgml/docbook/dsssl/modular/catalog \
-c /usr/local/share/sgml/docbook/catalog \
-c /usr/local/share/sgml/jade/catalog \
-d /usr/local/share/sgml/docbook/dsssl/modular/html/docbook.dsl
-t sgml file.sgml > file.html
Specifies the nochunks parameter to the
stylesheets, forcing all output to be written to
STDOUT (using Norm Walsh's stylesheets).
Specifies the catalogs that Jade will need to process.
Three catalogs are required. The first is a catalog that
contains information about the DSSSL stylesheets. The second
contains information about the DocBook DTD. The third contains
information specific to Jade.
Specifies the full path to the DSSSL stylesheet that Jade
will use when processing the document.
Instructs Jade to perform a
transformation from one DTD to another. In
this case, the input is being transformed from the DocBook DTD
to the HTML DTD.
Specifies the file that Jade should process, and redirects
output to the specified .html file.
Converting DocBook to HTML (several small files)
&prompt.user; jade \
-c /usr/local/share/sgml/docbook/dsssl/modular/catalog \
-c /usr/local/share/sgml/docbook/catalog \
-c /usr/local/share/sgml/jade/catalog \
-d /usr/local/share/sgml/docbook/dsssl/modular/html/docbook.dsl
-t sgml file .sgml
Specifies the catalogs that Jade will need to process.
Three catalogs are required. The first is a catalog that
contains information about the DSSSL stylesheets. The second
contains information about the DocBook DTD. The third contains
information specific to Jade.
Specifies the full path to the DSSSL stylesheet that Jade
will use when processing the document.
Instructs Jade to perform a
transformation from one DTD to another. In
this case, the input is being transformed from the DocBook DTD
to the HTML DTD.
Specifies the file that Jade should process. The
stylesheets determine how the individual HTML files will be
named, and the name of the root
file (i.e., the
one that contains the start of the document.
This example may still only generate one HTML file, depending on
the structure of the document you are processing, and the
stylesheet's rules for splitting output.
Converting DocBook to Postscript
The source SGML file must be converted to a TeX file.
&prompt.user; jade -Vtex-backend \
-c /usr/local/share/sgml/docbook/dsssl/modular/catalog \
-c /usr/local/share/sgml/docbook/catalog \
-c /usr/local/share/sgml/jade/catalog \
-d /usr/local/share/sgml/docbook/dsssl/modular/print/docbook.dsl
-t tex file .sgml
Customises the stylesheets to use various options
specific to producing output for TeX.
Specifies the catalogs that Jade will need to process. Three
catalogs are required. The first is a catalog that contains
information about the DSSSL stylesheets. The second contains
information about the DocBook DTD. The third contains
information specific to Jade.
Specifies the full path to the DSSSL stylesheet that
Jade will use when processing the document.
Instructs Jade to convert the output to TeX.
The generated .tex file must now be run
through tex , specifying the
&jadetex macro package.
&prompt.user; tex "&jadetex" file .tex
You have to run tex at
least three times. The first run processes the
document, and determines areas of the document which are referenced
from other parts of the document, for use in indexing, and so
on.
Do not be alarmed if you see warning messages such as
LaTeX Warning: Reference `136' on page 5 undefined on input
line 728. at this point.
The second run reprocesses the document now that certain pieces
of information are known (such as the document's page length). This
allows index entries and other cross-references to be fixed
up.
The third pass performs any final cleanup necessary.
The output from this stage will be
file .dvi .
Finally, run dvips to convert the
.dvi file to Postscript.
&prompt.user; dvips -o file .ps file.dvi
Converting DocBook to PDF
The first part of this process is identical to that when
converting DocBook to Postscript, using the same
jade command line ().
When the .tex file has been generated you
run TeX as before. However, use the &pdfjadetex macro package
instead.
&prompt.user; tex "&pdfjadetex" file .tex
Again, run this command three times.
This will generate
file .pdf , which does
not need to be processed any further.
diff --git a/en_US.ISO8859-1/books/fdp-primer/sgml-markup/chapter.sgml b/en_US.ISO8859-1/books/fdp-primer/sgml-markup/chapter.sgml
index ddf8ece3fa..e9cb2b13da 100644
--- a/en_US.ISO8859-1/books/fdp-primer/sgml-markup/chapter.sgml
+++ b/en_US.ISO8859-1/books/fdp-primer/sgml-markup/chapter.sgml
@@ -1,2563 +1,2563 @@
SGML Markup
This chapter describes the three markup languages you will encounter
when you contribute to the FreeBSD documentation project. Each section
describes the markup language, and details the markup that you are likely
to want to use, or that is already in use.
These markup languages contain a large number of elements, and it can
be confusing sometimes to know which element to use for a particular
situation. This section goes through the elements you are most likely to
need, and gives examples of how you would use them.
This is not an exhaustive list of elements, since
that would just reiterate the documentation for each language. The aim of
this section is to list those elements more likely to be useful to you.
If you have a question about how best to markup a particular piece of
content, please post it to the FreeBSD Documentation Project mailing list
freebsd-doc@FreeBSD.org .
Inline vs. block
In the remainder of this document, when describing elements,
inline means that the element can occur within a
block element, and does not cause a line break. A
block element, by comparison, will cause a line
break (and other processing) when it is encountered.
HTML
HTML, the HyperText Markup Language, is the markup language of
choice on the World Wide Web. More information can be found at
<URL:http://www.w3.org/ >.
HTML is used to markup pages on the FreeBSD web site. It should not
(generally) be used to mark up other documention, since DocBook offers a
far richer set of elements to choose from. Consequently, you will
normally only encounter HTML pages if you are writing for the web
site.
HTML has gone through a number of versions, 1, 2, 3.0, 3.2, and the
latest, 4.0 (available in both strict and
loose variants).
The HTML DTDs are available from the ports collection in the
textproc/html port. They are automatically
installed as part of the textproc/docproj
port.
Formal Public Identifier (FPI)
There are a number of HTML FPIs, depending upon the version (also
known as the level) of HTML that you want to declare your document to
be compliant with.
The majority of HTML documents on the FreeBSD web site comply with
the loose version of HTML 4.0.
PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
Sectional elements
An HTML document is normally split in to two sections. The first
section, called the head , contains
meta-information about the document, such as its title, the name of
the author, the parent document, and so on. The second section, the
body , contains the content that will be displayed
to the user.
These sections are indicated with head and
body elements respectively. These elements are
contained within the top-level html element.
Normal HTML document structure
<html>
<head>
<title>The document's title </title>
</head>
<body>
…
</body>
</html>
Block elements
Headings
HTML allows you to denote headings in your document, at up to
six different levels.
The largest and most prominent heading is h1 ,
then h2 , continuing down to
h6 .
The element's content is the text of the heading.
h1 , h2 , etc.
Use:
First section
This is the heading for the first section
This is the heading for the first sub-section
This is the heading for the second section
]]>
Generally, an HTML page should have one first level heading
(h1 ). This can contain many second level
headings (h2 ), which can in turn contain many
third level headings. Each
hn element should have
the same element, but one further up the hierarchy, preceeding it.
Leaving gaps in the numbering is to be avoided.
Bad ordering of
hn elements
Use:
First section
Sub-section
]]>
Paragraphs
HTML supports a single paragraph element,
p .
p
Use:
This is a paragraph. It can contain just about any
other element.
]]>
Block quotations
A block quotation is an extended quotation from another document
that should not appear within the current paragraph.
blockquote
Use:
A small excerpt from the US Constitution:
We the People of the United States, in Order to form
a more perfect Union, establish Justice, insure domestic
Tranquility, provide for the common defence, promote the general
Welfare, and secure the Blessings of Liberty to ourselves and our
Posterity, do ordain and establish this Constitution for the
United States of America. ]]>
Lists
You can present the user with three types of lists, ordered,
unordered, and definition.
Typically, each entry in an ordered list will be numbered, while
each entry in an unordered list will be preceded by a bullet point.
Definition lists are composed of two sections for each entry. The
first section is the term being defined, and the second section is
the definition of the term.
Ordered lists are indicated by the ol
element, unordered lists by the ul element, and
definition lists by the dl element.
Ordered and unordered lists contain listitems, indicated by the
li element. A listitem can contain textual
content, or it may be further wrapped in one or more
p elements.
Definition lists contain definition terms
(dt ) and definition descriptions
(dd ). A definition term can only contain inline
elements. A definition description can contain other block
elements.
ul and ol
Use:
An unordered list. Listitems will probably be
preceeded by bullets.
First item
Second item
Third item
An ordered list, with list items consisting of multiple
paragraphs. Each item (note: not each paragraph) will be
numbered.
This is the first item. It only has one paragraph.
This is the first paragraph of the second item.
This is the second paragraph of the second item.
This is the first and only paragraph of the third
item.
]]>
Definition lists with dl
Use:
Term 1
Paragraph 1 of definition 1.
Paragraph 2 of definition 1.
Term 2
Paragraph 1 of definition 2.
Term 3
Paragraph 1 of definition 3. Note that the <p>
element is not required in the single paragraph case.
]]>
Pre-formatted text
You can indicate that text should be shown to the user exactly
as it is in the file. Typically, this means that the text is shown
in a fixed font, multiple spaces are not merged in to one, and line
breaks in the text are significant.
In order to do this, wrap the content in the
pre element.
pre
You could use pre to mark up an e-mail
message;
From: nik@FreeBSD.org
To: freebsd-doc@FreeBSD.org
Subject: New documentation available
There's a new copy of my primer for contributers to the FreeBSD
Documentation Project available at
Comments appreciated.
N]]>
Tables
Most text-mode browsers (such as Lynx) do not render tables
particularly effectively. If you are relying on the tabular
display of your content, you should consider using alternative
markup to prevent confusion.
Mark up tabular information using the table
element. A table consists of one or more table rows
(tr ), each containing one or more cells of table
data (td ). Each cell can contain other block
elements, such as paragraphs or lists. It can also contain another
table (this nesting can repeat indefinitely). If the cell only
contains one paragraph then you do not need to include the
p element.
Simple use of table
Use:
This is a simple 2x2 table.
Top left cell
Top right cell
Bottom left cell
Bottom right cell
]]>
A cell can span multiple rows and columns. To indicate this,
add the rowspan and/or colspan
attributes, with values indicating the number of rows of columns
that should be spanned.
Using rowspan
Use:
One tall thin cell on the left, two short cells next to
it on the right.
Long and thin
Top cell
Bottom cell
]]>
Using colspan
Use:
One long cell on top, two short cells below it.
Top cell
Bottom left cell
Bottom right cell
]]>
Using rowspan and
colspan together
Use:
On a 3x3 grid, the top left block is a 2x2 set of
cells merged in to one. The other cells are normal.
Top left large cell
Top right cell
Middle right cell
Bottom left cell
Bottom middle cell
Bottom right cell
]]>
In-line elements
Emphasising information
You have two levels of emphasis available in HTML,
em and strong .
em is for a normal level of emphasis and
strong indicates stronger emphasis.
Typically, em is rendered in italic and
strong is rendered in bold. This is not always
the case, however, and you should not rely on it.
em and strong
Use:
This has been emphasised, while
this has been strongly emphasised.]]>
Bold and italics
Because HTML includes presentational markup, you can also
indicate that particular content should be rendered in bold or
italic. The elements are b and
i respectively.
b and i
This is in bold, while this is
in italics.]]>
Indicating fixed pitch text
If you have content that should be rendered in a fixed pitch
(typewriter) typeface, use tt (for
“teletype”).
tt
Use:
This document was originally written by
Nik Clayton, who can be reached by e-mail as
nik@FreeBSD.org .]]>
Content size
You can indicate that content should be shown in a larger or
smaller font. There are three ways of doing this.
Use big and small
around the content you wish to change size. These tags can be
nested, so <big><big>This is much
bigger</big></big> is possible.
Use font with the size
attribute set to +1 or -1
respectively. This has the same effect as using
big or small . However,
the use of this approach is deprecated.
Use font with the size
attribute set to a number between 1 and 7. The default font size
is 3 . This approach is deprecated.
big , small , and
font
The following fragments all do the same thing.
This text is slightly smaller . But
this text is slightly bigger .
This text is slightly smaller . But
this text is slightly bigger
This text is slightly smaller . But
this text is slightly bigger .
]]>
Links
Links are also in-line elements.
Linking to other documents on the WWW
In order to include a link to another document on the WWW you
must know the URL of the document you want to link to.
The link is indicated with a , and the
href attribute contains the URL of the target
document. The content of the element becomes the link, and is
normally indicated to the user in some way (underlining, change of
colour, different mouse cursor when over the link, and so
on).
Using <a href="...">
Use:
More information is available at the
FreeBSD web site .]]>
These links will take the user to the top of the chosen
document.
Linking to other parts of documents
Linking to a point within another document (or within the same
document) requires that the document author include anchors that you
can link to.
Anchors are indicated with a and the
name attribute instead of
href .
Using <a name="...">
Use:
This paragraph can be referenced
in other links with the name para1 .]]>
To link to a named part of a document, write a normal link to
that document, but include the name of the anchor after a
# symbol.
Linking to a named part of another document
Assume that the para1 example resides in a
document called foo.html .
More information can be found in the
first paragraph of
foo.html .]]>
If you are linking to a named anchor within the same document
then you can omit the document's URL, and just include the name of
the anchor (with the preceeding # ).
Linking to a named part of the same document
Assume that the para1 example resides in
this document
More information can be found in the
first paragraph of this
document.]]>
DocBook
DocBook was designed by the Davenport Group to be
a DTD for writing technical documentation. As such, and unlike LinuxDoc
and HTML, DocBook is very heavily oriented towards markup that
describes what something is, rather than describing
how it should be presented.
formal vs. informal
Some elements may exist in two forms, formal
and informal . Typically, the formal version of
the element will consist of a title followed by the information
version of the element. The informal version will not have a
title.
The DocBook DTD is available from the ports collection in the
textproc/docbook port. It is automatically
installed as part of the textproc/docproj
port.
FreeBSD extensions
The FreeBSD Documentation Project has extended the DocBook DTD by
adding some new elements. These elements serve to make some of the
markup more precise.
Where a FreeBSD specific element is listed below it is clearly
marked.
Throughout the rest of this document, the term
“DocBook” is used to mean the FreeBSD extended DocBook
DTD.
There is nothing about these extensions that is FreeBSD
specific, it was just felt that they were useful enhancements for
this particular project. Should anyone from any of the other *nix
camps (NetBSD, OpenBSD, Linux, …) be interested in
collaborating on a standard DocBook extension set, please get in
touch with Nik Clayton nik@FreeBSD.org .
The FreeBSD extensions are not (currently) in the ports
collection. They are stored in the FreeBSD CVS tree, as doc/share/sgml/freebsd.dtd .
Formal Public Identifier (FPI)
In compliance with the DocBook guidelines for writing FPIs for
DocBook customisations, the FPI for the FreeBSD extended DocBook DTD
is;
PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN"
Document structure
DocBook allows you to structure your documentation in several
ways. In the FreeBSD Documentation Project we are using two primary
types of DocBook document: the book and the article.
A book is organised into chapter s. This is a
mandatory requirement. There may be part s between
the book and the chapter to provide another layer of organisation.
The Handbook is arranged in this way.
A chapter may (or may not) contain one or more sections. These
are indicated with the sect1 element. If a section
contains another section then use the sect2
element, and so on, up to sect5 .
Chapters and sections contain the remainder of the content.
An article is simpler than a book, and does not use chapters.
Instead, the content of an article is organised into one or more
sections, using the same sect1 (and
sect2 and so on) elements that are used in
books.
Obviously, you should consider the nature of the documentation you
are writing in order to decide whether it is best marked up as a book
or an article. Articles are well suited to information that does not
need to be broken down into several chapters, and that is, relatively
speaking, quite short, at up to 20-25 pages of content. Books are
best suited to information that can be broken up into several
chapters, possibly with appendices and similar content as well.
The FreeBSD
tutorials are all marked up as articles, while this
document, the FreeBSD
FAQ , and the FreeBSD Handbook are
all marked up as books.
Starting a book
The content of the book is contained within the
book element. As well as containing structural
markup, this element can contain elements that include additional
information about the book. This is either meta-information, used
for reference purposes, or additional content used to produce a
title page.
This additional information should be contained within
bookinfo .
Boilerplate book with
bookinfo
<book>
<bookinfo>
<title>Your title here </title>
<author>
<firstname>Your first name </firstname>
<surname>Your surname </surname>
<affiliation>
<address><email>Your e-mail address </email></address>
</affiliation>
</author>
<copyright>
<year>1998 </year>
<holder role="mailto:your e-mail address ">Your name </holder>
</copyright>
<pubdate role="rcs">$Date$</pubdate>
<releaseinfo>$Id$</releaseinfo>
<abstract>
<para>Include an abstract of the book's contents here. </para>
</abstract>
</bookinfo>
…
</book>
Starting an article
The content of the article is contained within the
article element. As well as containing
structural markup, this element can contain elements that include
additional information about the article. This is either
meta-information, used for reference purposes, or additional content
used to produce a title page.
This additional information should be contained within
articleinfo .
Boilerplate article with
articleinfo
<article>
<articleinfo>
<title>Your title here </title>
<author>
<firstname>Your first name </firstname>
<surname>Your surname </surname>
<affiliation>
<address><email>Your e-mail address </email></address>
</affiliation>
</author>
<copyright>
<year>1998 </year>
<holder role="mailto:your e-mail address ">Your name </holder>
</copyright>
<pubdate role="rcs">$Date$</pubdate>
<releaseinfo>$Id$</releaseinfo>
<abstract>
<para>Include an abstract of the article's contents here. </para>
</abstract>
</articleinfo>
…
</article>
Indicating chapters
Use chapter to mark up your chapters. Each
chapter has a mandatory title . Articles do not
contain chapters, they are reserved for books.
A simple chapter
The chapter's title
...
]]>
A chapter cannot be empty; it must contain elements in addition
to title . If you need to include an empty
chapter then just use an empty paragraph.
Empty chapters
This is an empty chapter
]]>
Sections below chapters
In books, chapters may (but do not need to) be broken up into
sections, subsections, and so on. In articles, sections are the
main structural element, and each article must contain at least one
section. Use the
sectn element. The
n indicates the section number, which
identifies the section level.
The first sectn is
sect1 . You can have one or more of these in a
chapter. They can contain one or more sect2
elements, and so on, down to sect5 .
Sections in chapters
A sample chapter
Some text in the chapter.
First section (1.1)
…
Second section (1.2)
First sub-section (1.2.1)
First sub-sub-section (1.2.1.1)
…
Second sub-section (1.2.2)
…
]]>
This example includes section numbers in the section titles.
You should not do this in your documents. Adding the section
numbers is carried out the by the stylesheets (of which more
later), and you do not need to manage them yourself.
Subdividing using part s
You can introduce another layer of organisation between
book and chapter with one or
more part s. This cannot be done in an
article .
Introduction
Overview
...
What is FreeBSD?
...
History
...
]]>
Block elements
Paragraphs
DocBook supports three types of paragraphs:
formalpara , para , and
simpara .
Most of the time you will only need to use
para . formalpara includes a
title element, and simpara
disallows some elements from within para . Stick
with para .
para
Use:
This is a paragraph. It can contain just about any
other element. ]]>
Appearance:
This is a paragraph. It can contain just about any other
element.
Block quotations
A block quotation is an extended quotation from another document
that should not appear within the current paragraph. You will
probably only need it infrequently.
Blockquotes can optionally contain a title and an attribution
(or they can be left untitled and unattributed).
blockquote
Use:
A small excerpt from the US Constitution;
Preamble to the Constitution of the United States
Copied from a web site somewhere
We the People of the United States, in Order to form a more perfect
Union, establish Justice, insure domestic Tranquility, provide for the
common defence, promote the general Welfare, and secure the Blessings
of Liberty to ourselves and our Posterity, do ordain and establish this
Constitution for the United States of America.
]]>
Appearance:
Preamble to the Constitution of the United States
Copied from a web site somewhere
We the People of the United States, in Order to form a more
perfect Union, establish Justice, insure domestic Tranquility,
provide for the common defence, promote the general Welfare, and
secure the Blessings of Liberty to ourselves and our Posterity,
do ordain and establish this Constitution for the United States
of America.
Tips, notes, warnings, cautions, important information and
sidebars.
You may need to include extra information separate from the
main body of the text. Typically this is “meta”
information that the user should be aware of.
Depending on the nature of the information, one of
tip , note ,
warning , caution , and
important should be used. Alternatively, if the
information is related to the main text but is not one of the above,
use sidebar .
The circumstances in which to choose one of these elements over
another is unclear. The DocBook documentation suggests;
A Note is for information that should be heeded by all
readers.
An Important element is a variation on Note.
A Caution is for information regarding possible data loss
or software damage.
A Warning is for information regarding possible hardware
damage or injury to life or limb.
warning
Use:
Installing FreeBSD may make you want to delete Windows from your
harddisk.
]]>
Installing FreeBSD may make you want to delete Windows from
your harddisk.
Lists and procedures
You will often need to list pieces of information to the user,
or present them with a number of steps that must be carried out in
order to accomplish a particular goal.
In order to do this, use itemizedlist ,
orderedlist , or
procedure There are other types of
list element in DocBook, but we're not concerned with those at
the moment.
itemizedlist and
orderedlist are similar to their counterparts in
HTML, ul and ol . Each one
consists of one or more listitem elements, and
each listitem contains one or more block
elements. The listitem elements are analagous to
HTML's li tags. However, unlike HTML, they are
required.
procedure is slightly different. It consists
of step s, which may in turn consists of more
step s or substep s. Each
step contains block elements.
itemizedlist ,
orderedlist , and
procedure
Use:
This is the first itemized item.
This is the second itemized item.
This is the first ordered item.
This is the second ordered item.
Do this.
Then do this.
And now do this.
]]>
Appearance:
This is the first itemized item.
This is the second itemized item.
This is the first ordered item.
This is the second ordered item.
Do this.
Then do this.
And now do this.
Showing file samples
If you want to show a fragment of a file (or perhaps a complete
file) to the user, wrap it in the programlisting
element.
White space and line breaks within
programlisting are
significant. In particular, this means that the opening tag should
appear on the same line as the first line of the output, and the
closing tag should appear on the same line as the last line of the
output, otherwise spurious blank lines may be included.
programlisting
Use:
When you have finished, your program should look like
this;
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
} ]]>
Notice how the angle brackets in the
#include line need to be referenced by their
entities instead of being included literally.
Appearance:
When you have finished, your program should look like
this;
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
}
Callouts
A callout is a mechanism for referring back to an earlier piece
of text or specific position within an earlier example without
linking to it within the text.
To do this, mark areas of interest in your example
(programlisting ,
literallayout , or whatever) with the
co element. Each element must have a unique
id assigned to it. After the example include a
calloutlist that refers back to the example and
provides additional commentary.
co and
calloutlist
When you have finished, your program should look like
this;
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
}
Includes the standard IO header file.
Specifies that main() returns an
int.
The printf() call that writes
hello, world to standard output.
]]>
Appearance:
When you have finished, your program should look like
this;
#include <stdio.h>
int
main(void)
{
printf("hello, world\n");
}
Includes the standard IO header file.
Specifies that main() returns an
int.
The printf() call that writes
hello, world to standard output.
Tables
Unlike HTML, you do not need to use tables for layout purposes,
as the stylesheet handles those issues for you. Instead, just use
tables for marking up tabular data.
In general terms (and see the DocBook documentation for more
detail) a table (which can be either formal or informal) consists of
a table element. This contains at least one
tgroup element, which specifies (as an attribute)
the number of columns in this table group. Within the tablegroup
you can then have one thead element, which
contains elements for the table headings (column headings), and one
tbody which contains the body of the
table.
Both tgroup and thead
contain row elements, which in turn contain
entry elements. Each entry
element specifies one cell in the table.
informaltable
Use:
This is column head 1
This is column head 2
Row 1, column 1
Row 1, column 2
Row 2, column 1
Row 2, column 2
]]>
Appearance:
This is column head 1
This is column head 2
Row 1, column 1
Row 1, column 2
Row 2, column 1
Row 2, column 2
If you don't want a border around the table the
frame attribute can be added to the
informaltable element with a value of
none (i.e., <informaltable
frame="none"> ).
Tables where frame="none"
Appearance:
This is column head 1
This is column head 2
Row 1, column 1
Row 1, column 2
Row 2, column 1
Row 2, column 2
Examples for the user to follow
A lot of the time you need to show examples for the user to
follow. Typically, these will consist of dialogs with the computer;
the user types in a command, the user gets a response back, they
type in another command, and so on.
A number of distinct elements and entities come in to play
here.
screen
Everything the user sees in this example will be on the
computer screen, so the next element is
screen .
Within screen , white space is
significant.
prompt ,
&prompt.root; and
&prompt.user;
Some of the things the user will be seeing on the screen
are prompts from the computer (either from the OS, command
shell, or application. These should be marked up using
prompt .
As a special case, the two shell prompts for the normal
user and the root user have been provided as entities. Every
time you want to indicate the user is at a shell prompt, use
one of &prompt.root; and
&prompt.user; as necessary. They do
not need to be inside prompt .
&prompt.root; and
&prompt.user; are FreeBSD
extensions to DocBook, and are not part of the original
DTD.
userinput
When displaying text that the user should type in, wrap it
in userinput tags. It will probably be
displayed differently to the user.
screen , prompt , and
userinput
Use:
&prompt.user; ls -1
foo1
foo2
foo3
&prompt.user; ls -1 | grep foo2
foo2
&prompt.user; su
Password:
&prompt.root; cat foo2
This is the file called 'foo2']]>
Appearance:
&prompt.user; ls -1
foo1
foo2
foo3
&prompt.user; ls -1 | grep foo2
foo2
&prompt.user; su
Password:
&prompt.root; cat foo2
This is the file called 'foo2'
Even though we are displaying the contents of the file
foo2 , it is not marked
up as programlisting . Reserve
programlisting for showing fragments of files
outside the context of user actions.
In-line elements
Emphasising information
When you want to emphasise a particular word or phrase, use
emphasis . This may be presented as italic, or
bold, or might be spoken differently with a text-to-speech
system.
There is no way to change the presentation of the emphasis
within your document, no equivalent of HTML's b
and i . If the information you are presenting is
important then consider presenting it in
important rather than
emphasis .
emphasis
Use:
FreeBSD is without doubt the
premiere Unix like operating system for the Intel architecture.]]>
Appearance:
FreeBSD is without doubt the premiere Unix
like operating system for the Intel architecture.
Applications, commands, options, and cites
You will frequently want to refer to both applications and
commands when writing for the Handbook. The distinction between
them is simple: an application is the name for a suite (or possibly
just 1) of programs that fulfil a particular task. A command is the
name of a program that the user can run.
In addition, you will occasionally need to list one or more of
the options that a command might take.
Finally, you will often want to list a command with its manual
section number, in the “command(number)” format so
common in Unix manuals.
Mark up application names with
application .
When you want to list a command with its manual section number
(which should be most of the time) the DocBook element is
citerefentry . This will contain a further two
elements, refentrytitle and
manvolnum . The content of
refentrytitle is the name of the command, and the
content of manvolnum is the manual page
section.
This can be cumbersome to write, and so a series of general entities
have been created to make this easier. Each entity takes the form
&man.manual-page .manual-section ; .
The file that contains these entities is in
doc/share/sgml/man-refs.ent , and can be
referred to using this FPI:
PUBLIC "-//FreeBSD//ENTITIES DocBook Manual Page Entities//EN"
Therefore, the introduction to your documentation will probably
look like this:
<!DOCTYPE book PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
<!ENTITY % man PUBLIC "-//FreeBSD//ENTITIES DocBook Manual Page Entities//EN">
%man;
…
]>
Use command when you want to include a
command name “in-line” but present it as something the
user should type in.
Use option to mark up a command's
options.
This can be confusing, and sometimes the choice is not always
clear. Hopefully this example makes it clearer.
Applications, commands, and options.
Use:
Sendmail is the most
widely used Unix mail application.
Sendmail includes the
sendmail
8
, &man.mailq.8;, and &man.newaliases.8;
programs.
One of the command line parameters to
sendmail
8
, -bp , will display the current
status of messages in the mail queue. Check this on the command
line by running sendmail -bp . ]]>
Appearance:
Sendmail is the most widely used
Unix mail application.
Sendmail includes the
sendmail
8
,
mailq
8
, and
newaliases
8
programs.
One of the command line parameters to
sendmail
8
, -bp , will display the current
status of messages in the mail queue. Check this on the command
line by running sendmail -bp .
Notice how the
&man.command .section ; notation is easier to follow.
Files, directories, extensions
Whenever you wish to refer to the name of a file, a directory,
or a file extension, use filename .
filename
Use:
The SGML source for the Handbook in English can be
found in /usr/doc/en/handbook/ . The first
file is called handbook.sgml in that
directory. You should also see a Makefile
and a number of files with a .ent
extension.]]>
Appearance:
The SGML source for the Handbook in English can be found in
/usr/doc/en/handbook/ . The first file is
called handbook.sgml in that directory. You
should also see a Makefile and a number of
files with a .ent extension.
Devices
FreeBSD extension
These elements are part of the FreeBSD extension to DocBook,
and do not exist in the original DocBook DTD.
When referring to devices you have two choices. You can either
refer to the device as it appears in /dev , or
you can use the name of the device as it appears in the kernel. For
this latter course, use devicename .
Sometimes you will not have a choice. Some devices, such as
networking cards, do not have entries in /dev ,
or the entries are markedly different from those entries.
devicename
Use:
sio is used for serial
communication in FreeBSD. sio manifests
through a number of entries in /dev , including
/dev/ttyd0 and /dev/cuaa0 .
By contrast, the networking devices, such as
ed0 do not appear in /dev .
In MS-DOS, the first floppy drive is referred to as
a: . In FreeBSD it is
/dev/fd0 . ]]>
Appearance:
sio is used for serial communication
in FreeBSD. sio manifests through a
number of entries in /dev , including
/dev/ttyd0 and
/dev/cuaa0 .
By contrast, the networking devices, such as
ed0 do not appear in
/dev .
In MS-DOS, the first floppy drive is referred to as
a: . In FreeBSD it is
/dev/fd0 .
Hosts, domains, IP addresses, and so forth
FreeBSD extension
These elements are part of the FreeBSD extension to DocBook,
and do not exist in the original DocBook DTD.
You can markup identification information for networked
computers (hosts) in several ways, depending on the nature of the
information. All of them use hostid as the
element, with the role attribute selecting the
type of the marked up information.
No role attribute, or
role="hostname"
With no role attribute (i.e.,
hostid ...hostid the
marked up information is the simple hostname, such as
freefall or wcarchive .
You can explicitly specify this with
role="hostname" .
role="domainname"
The text is a domain name, such as
FreeBSD.org or
ngo.org.uk . There is no hostname
component.
role="fqdn"
The text is a Fully Qualified Domain Name, with both
hostname and domain name parts.
role="ipaddr"
The text is an IP address, probably expressed as a dotted
quad.
role="ip6addr"
The text is an IPv6 address.
role="netmask"
The text is a network mask, which might be expressed as a
dotted quad, a hexadecimal string, or as a
/ followed by a number.
role="mac"
The text is an ethernet MAC address, expressed as a series
of 2 digit hexadecimal numbers seperated by colons.
hostid and roles
Use:
The local machine can always be referred to by the
name localhost , which will have the IP address
127.0.0.1 .
The FreeBSD.org domain
contains a number of different hosts, including
freefall.FreeBSD.org and
bento.FreeBSD.org .
When adding an IP alias to an interface (using
ifconfig ) always use a
netmask of 255.255.255.255
(which can also be expressed as 0xffffffff .
The MAC address uniquely identifies every network card in
in existence. A typical MAC address looks like 08:00:20:87:ef:d0 . ]]>
Appearance:
The local machine can always be referred to by the name
localhost , which will have the IP address 127.0.0.1 .
The FreeBSD.org domain
contains a number of different hosts, including freefall.FreeBSD.org and bento.FreeBSD.org .
When adding an IP alias to an interface (using
ifconfig ) always use a
netmask of 255.255.255.255 (which
can also be expressed as 0xffffffff .
The MAC address uniquely identifies every network card in
existence. A typical MAC address looks like 08:00:20:87:ef:d0 .
Usernames
FreeBSD extension
These elements are part of the FreeBSD extension to DocBook,
and do not exist in the original DocBook DTD.
When you need to refer to a specific username, such as
root or bin , use
username .
username
Use:
To carry out most system administration functions you
will need to be root .]]>
Appearance:
To carry out most system administration functions you will
need to be root .
Describing Makefile s
FreeBSD extension
These elements are part of the FreeBSD extension to DocBook,
and do not exist in the original DocBook DTD.
Two elements exist to describe parts of
Makefile s, maketarget and
makevar .
maketarget identifies a build target exported
by a Makefile that can be given as a parameter
to make . makevar identifies a
variable that can be set (in the environment, on the
make command line, or within the
Makefile ) to influence the process.
maketarget and
makevar
Use:
Two common targets in a Makefile
are all and clean .
Typically, invoking all will rebuild the
application, and invoking clean will remove
the temporary files (.o for example) created by
the build process.
clean may be controlled by a number of
variables, including CLOBBER and
RECURSE . ]]>
Appearance:
Two common targets in a Makefile are
all and
clean .
Typically, invoking all will rebuild
the application, and invoking clean will
remove the temporary files (.o for example)
created by the build process.
clean may be controlled by a number
of variables, including CLOBBER and
RECURSE .
Literal text
You will often need to include “literal” text in the
Handbook. This is text that is excerpted from another file, or
which should be copied from the Handbook into another file
verbatim.
Some of the time, programlisting will be
sufficient to denote this text. programlisting
is not always appropriate, particularly when you want to include a
portion of a file “in-line” with the rest of the
paragraph.
On these occasions, use literal .
literal
Use:
The maxusers 10 line in the kernel
configuration file determines the size of many system tables, and is
a rough guide to how many simultaneous logins the system will
support.]]>
Appearance:
The maxusers 10 line in the kernel
configuration file determines the size of many system tables, and
is a rough guide to how many simultaneous logins the system will
support.
Showing items that the user must fill
in
There will often be times when you want to show the user what to
do, or refer to a file, or command line, or similar, where the user
can not simply copy the examples that you provide, but must instead
include some information themselves.
replaceable is designed for this eventuality.
Use it inside other elements to indicate parts
of that element's content that the user must replace.
replaceable
Use:
&prompt.user; man command
]]>
Appearance:
&prompt.user; man command
replaceable can be used in many different
elements, including literal . This example also
shows that replaceable should only be wrapped
around the content that the user is meant to
provide. The other content should be left alone.
Use:
The maxusers n
line in the kernel configuration file determines the size of many system
tables, and is a rough guide to how many simultaneous logins the system will
support.
For a desktop workstation, 32 is a good value
for n . ]]>
Appearance:
The maxusers n
line in the kernel configuration file determines the size of many
system tables, and is a rough guide to how many simultaneous
logins the system will support.
For a desktop workstation, 32 is a good
value for n .
Images
Image support in the documentation is currently extremely
experimental. I think the mechanisms described here are unlikely to
change, but that's not guaranteed.
You will also need to install the
graphics/ImageMagick port, which is used to
convert between the different image formats. This is a big port,
and most of it is not required. However, while we're working on the
Makefile s and other infrastructure it makes
things easier. This port is not in the
textproc/docproj meta port, you must install it
by hand.
The best example of what follows in practice is the
en_US.ISO8859-1/articles/vm-design/ document.
If you're unsure of the description that follows, take a look at the
files in that directory to see how everything hangs togther.
Experiment with creating different formatted versions of the
document to see how the image markup appears in the formatted
output.
Image formats
We currently support two formats for images. The format you
should use will depend on the nature of your image.
For images that are primarily vector based, such as network
diagrams, timelines, and similar, use Encapsulated Postscript, and
make sure that your images have the .eps
extension.
For bitmaps, such as screen captures, use the Portable Network
Graphic format, and make sure that your images have the
.png extension.
These are the only formats in which images
should be committed to the CVS repository.
Use the right format for the right image. It is to be expected
that your documentation will have a mix of EPS and PNG images. The
Makefile s ensure that the correct format image
is chosen depending on the output format that you use for your
documentation. Do not commit the same image to the
repository in two different formats .
It is anticipated that the Documentation Project will switch to
using the Scalable Vector Graphic (SVG) format for vector images.
However, the current state of SVG capable editing tools makes this
impractical.
Markup
The markup for an image is relatively simple. First, markup a
mediaobject . The mediaobject
can contain other, more specific objects. We are concerned with
two, the imageobject and the
textobject .
You should include one imageobject , and two
textobject elements. The
imageobject will point to the name of the image
file that will be used (without the extension). The
textobject elements contain information that will
be presented to the user as well as, or instead of, the
image.
There are two circumstances where this can happen.
When the reader is viewing the documentation in HTML. In
this case, each image will need to have associated alternate
text to show the user, typically whilst the image is loading, or
if they hover the mouse pointer over the image.
When the reader is viewing the documentation in plain text.
In this case, each image should have an ASCII art equivalent to
show the user.
An example will probably make things easier to understand.
Suppose you have an image, called fig1 , that
you want to include in the document. This image is of a rectangle
with an A inside it. The markup for this would be as
follows.
<mediaobject>
<imageobject>
<imagedata fileref="fig1">
</imageobject>
<textobject>
<literallayout class="monospaced">+---------------+
| A |
+---------------+</literallayout>
</textobject>
<textobject>
<phrase>A picture</phrase>
</textobject>
</mediaobject>
Include an imagedata element inside the
imageobject element. The
fileref attribute should contain the filename
of the image to include, without the extension. The stylesheets
will work out which extension should be added to the filename
automatically.
The first textobject should contain a
literallayout element, where the
class attribute is set to
monospaced . This is your opportunity to
demonstrate your ASCII art skills. This content will be used if
the document is converted to plain text.
Notice how the first and last lines of the content of the
literallayout element butt up next to the
element's tags. This ensures no extraneous white space is
included.
The second textobject should contain a
single phrase element. The contents of this
will become the alt attribute for the image
when this document is converted to HTML.
Makefile entries
Your images must be listed in the
Makefile in the IMAGES
variable. This variable should contain the name of all your
source images. For example, if you have
created three figures, fig1.eps ,
fig2.png , fig3.png , then
your Makefile should have lines like this in
it.
…
IMAGES= fig1.eps fig2.png fig3.png
…
or
…
IMAGES= fig1.eps
IMAGES+= fig2.png
IMAGES+= fig3.png
…
Again, the Makefile will work out the
complete list of images it needs to build your source document, you
only need to list the image files you
provided.
Images and chapters in subdirectories
You must be careful when you separate your documentation in to
smaller files (see ) in
different directories.
Suppose you have a book with three chapters, and the chapters
are stored in their own directories, called
chapter1/chapter.sgml ,
chapter2/chapter.sgml , and
chapter3/chapter.sgml . If each chapter has
images associated with it, I suggest you place those images in each
chapter's subdirectory (chapter1/ ,
chapter2/ , and
chapter3/ ).
However, if you do this you must include the directory names in
the IMAGES variable in the
Makefile , and you must
include the directory name in the imagedata
element in your document.
For example, if you have chapter1/fig1.png ,
then chapter1/chapter.sgml should
contain
<mediaobject>
<imageobject>
<imagedata fileref="chapter1/fig1">
</imageobject>
…
</mediaobject>
The directory name must be included in the
fileref attribute
The Makefile must contain
…
IMAGES= chapter1/fig1.png
…
Then everything should just work.
Links
Links are also in-line elements.
Linking to other parts of the same document
- Linking within the same document requires you to to specify
+ Linking within the same document requires you to specify
where you are linking from (i.e., the text the user will click, or
otherwise indicate, as the source of the link) and where you are
linking to (the link's destination).
Each element within DocBook has an attribute called
id . You can place text in this attribute to
uniquely name the element it is attached to.
This value will be used when you specify the link
source.
Normally, you will only be linking to chapters or sections, so
you would add the id attribute to these
elements.
id on chapters and sections
Introduction
This is the introduction. It contains a subsection,
which is identified as well.
Sub-sect 1
This is the subsection.
]]>
Obviously, you should use more descriptive values. The values
must be unique within the document (i.e., not just the file, but the
document the file might be included in as well). Notice how the
id for the subsection is constructed by appending
text to the id of the chapter. This helps to
ensure that they are unique.
If you want to allow the user to jump into a specific portion of
the document (possibly in the middle of a paragraph or an example),
use anchor . This element has no content, but
takes an id attribute.
anchor
This paragraph has an embedded
link target in it. It won't show up in
the document. ]]>
When you want to provide the user with a link they can activate
(probably by clicking) to go to a section of the document that has
an id attribute, you can use either
xref or link .
Both of these elements have a linkend
attribute. The value of this attribute should be the value that you
have used in a id attribute (it does not matter
if that value has not yet occurred in your document; this will work
for forward links as well as backward links).
If you use xref then you have no control over
the text of the link. It will be generated for you.
Using xref
Assume that this fragment appears somewhere in a document that
includes the id example;
More information can be found
in .
More specific information can be found
in . ]]>
The text of the link will be generated automatically, and will
look like (emphasised text indicates the text
that will be the link);
More information can be found in Chapter
One .
More specific information can be found in the
section called Sub-sect 1 .
Notice how the text from the link is derived from the section
title or the chapter number.
This means that you can not use
xref to link to an id
attribute on an anchor element. The
anchor has no content, so the
xref can not generate the text for the
link.
If you want to control the text of the link then use
link . This element wraps content, and the
content will be used for the link.
Using link
Assume that this fragment appears somewhere in a document that
includes the id example.
More information can be found in
the first chapter.
More specific information can be found in
FreeBSD
home page instead. ]]>
Appearance:
Of course, you could stop reading this document and go to the
FreeBSD home page
instead.
* LinuxDoc
LinuxDoc is an adaptation of the QWERTZ DTD, first adopted by the
Linux Documentation
Project , and subsequently adopted by the FreeBSD Documentation
Project.
The LinuxDoc DTD contains primarily appearance related markup rather
than content related markup (i.e., it describes what something looks
like rather than what it is).
Both the FreeBSD Documentation Project and the Linux Documentation
Project are migrating from the LinuxDoc DTD to the DocBook DTD.
The LinuxDoc DTD is available from the ports collection in the
textproc/linuxdoc category.
diff --git a/en_US.ISO8859-1/books/handbook/boot/chapter.sgml b/en_US.ISO8859-1/books/handbook/boot/chapter.sgml
index d1c2c988c8..95676520eb 100644
--- a/en_US.ISO8859-1/books/handbook/boot/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/boot/chapter.sgml
@@ -1,549 +1,549 @@
The FreeBSD Booting Process
Synopsis
FreeBSD uses a three-stage bootstrap by default, which
basically entails three programs which call each
other in order (two boot
blocks, and the loader). Each of these three build on the
previous program's understanding and provide increasing amounts
of sophistication.
The kernel is then started, which will then probe for devices
and initialize them for use. Once the kernel boot
process is finished, the kernel passes control to the user process
&man.init.8;, which then makes sure the disks are in a usable state.
&man.init.8; then starts the user-level resource configuration which
then mounts filesystems, sets up network cards to act on the
network, and generally starts all the processes that usually
are run on a FreeBSD system at startup.
The Boot Blocks: Bootstrap Stages 1 and 2
Bootstrapping is the process
whereby a computer probes and initializes its devices, and
works out what programs it is supposed to run.
This involves the use of special Read Only Memory chips,
which determine what further operations to do, and these
usually pass control to other chips that do consistency and
memory tests, configure devices, and provide a mechanism for
programs to determine what configuration details were
determined.
In standard personal computers, this involves the BIOS
(which oversees the bootstrap), and CMOS (which stores
configuration). BIOS and CMOS understand disks, and also
understand where on the disk to find a program that will know
how to load up an operating system.
This chapter will not deal with this first part of the
bootstrap process. Instead it will focus on what happens after control
is passed to the program on the disk.
The boot blocks are responsible for finding (usually) the
loader, and running it, and thus need to understand how to
find that program on the filesystem, how to run the program,
and also allow minor configuration of how they work.
boot0
There is actually a preceding bootblock, named boot0,
which lives on the Master Boot
Record , the special part of the disk that the
system bootstrap looks for and runs, and it simply shows a
list of possible slices to boot from.
boot0 is very simple, since the program in the
MBR can only be 512 bytes in size.
It displays something like this:
boot0 screenshot
F1 DOS
F2 FreeBSD
F3 Linux
F4 ??
F5 Drive 1
Default: F2
boot1
boot1 is found on the boot sector of the boot slice,
which is where boot0, or
any other program on the MBR expects to
find the program to run to continue the boot process.
boot1 is very simple, since it too can only be 512 bytes
in size, and knows just enough about the FreeBSD
disklabel , which stores information
about the slice, to find and execute boot2.
boot2
boot2 is slightly more sophisticated, and understands
the FreeBSD filesystem enough to find files on it, and can
provide a simple interface to choose the kernel or loader to
run.
Since the loader is
much more sophisticated, and provides a nice easy-to-use
boot configuration, boot2 usually runs it, but previously it
was tasked to run the kernel directly.
boot2 screenshot
>> FreeBSD/i386 BOOT
Default: 0:wd(0,a)/kernel
boot:
Loader: Bootstrap Stage Three
The loader is the final stage of the three-stage
bootstrap, and is located on the filesystem, usually as
/boot/loader .
While /boot/boot0 ,
/boot/boot1 , and
/boot/boot2 are files there, they are
not the actual copies in the MBR , the boot
sector, or the disklabel respectively.
The loader is intended as a user-friendly method for
configuration, using an easy-to-use built-in command set,
backed up by a more powerful interpreter, with a more complex
command set.
Loader Program Flow
During initialization, the loader will probe for a
console and for disks, and figure out what disk it is
booting from. It will set variables accordingly, and then
the interpreter is started, and the easy-to-use commands are
explained to it.
loader will then read
/boot/loader.rc , which by default reads
in /boot/defaults/loader.conf which
sets reasonable defaults for variables and reads
/boot/loader.conf for local changes to
those variables. loader.rc then acts
on these variables, loading whichever modules and kernel are
selected.
Finally, by default, the loader issues a 10 second wait
for key presses, and boots the kernel if it is not interrupted.
If interrupted, the user is presented with a prompt which
understands the easy-to-use command set, where the user may
adjust variables, unload all modules, load modules, and then
finally boot or reboot.
A more technical discussion of the process is available
in &man.loader.8;
Loader Built-In Commands
The easy-to-use command set comprises of:
autoboot seconds
Proceeds to boot the kernel if not interrupted
within the time span given, in seconds. It displays a
countdown, and the default timespan is 10
seconds.
boot
-options
kernelname
Immediately proceeds to boot the kernel, with the
given options, if any, and with the kernel name given,
if it is.
boot-conf
Goes through the same automatic configuration of
modules based on variables as what happens at boot.
This only makes sense if you use
unload first, and change some
variables, most commonly kernel .
help
topic
Shows help messages read from
/boot/loader.help . If the topic
given is index , then the list of
available topics is given.
include filename
…
Processes the file with the given filename. The
file is read in, and interpreted line by line. An
error immediately stops the include command.
load -t
type
filename
Loads the kernel, kernel module, or file of the
type given, with the filename given. Any arguments
after filename are passed to the file.
ls -l
path
Displays a listing of files in the given path, or
the root directory, if the path is not specified. If
-l is specified, file sizes will be
shown too.
lsdev -v
Lists all of the devices from which it may be
possible to load modules. If -v is
specified, more details are printed.
lsmod -v
Displays loaded modules. If -v is
specified, more details are shown.
more filename
Display the files specified, with a pause at each
LINES displayed.
reboot
Immediately reboots the system.
set variable
set
variable =value
Set loader's environment variables.
unload
Removes all loaded modules.
Loader Examples
Here are some practical examples of loader usage.
To simply boot your usual kernel, but in single-user
mode:
boot -s
To unload your usual kernel and modules, and then
load just your old (or another) kernel:
unload
load kernel.old
You can use kernel.GENERIC to
refer to the generic kernel that comes on the install
disk, or kernel.old to refer to
your previously installed kernel (when you've upgraded
or configured your own kernel, for example).
Use the following to load your usual modules with
another kernel:
unload
set kernel="kernel.old "
boot-conf
To load a kernel configuration script (an automated
script which does the things you'd normally do in the
kernel boot-time configurator):
load -t userconfig_script
/boot/kernel.conf
Kernel Interaction During Boot
Once the kernel is loaded by either loader (as usual) or boot2 (bypassing the loader), it
examines its boot flags, if any, and adjusts its behavior as
necessary.
Kernel Boot Flags
Here are the more common boot flags:
-a
during kernel initialization, ask for the device
- to mount as as the root file system.
+ to mount as the root file system.
-C
boot from CDROM.
-c
run UserConfig, the boot-time kernel
configurator
-s
boot into single-user mode
-v
be more verbose during kernel startup
There are other boot flags, read &man.boot.8; for more
information on them.
Init: Process Control Initialization
Once the kernel has finished booting, it passes control to
the user process init , which is located at
/sbin/init , or the program path specified
in the init_path variable in
loader .
Automatic Reboot Sequence
The automatic reboot sequence makes sure that the
filesystems available on the system are consistent. If they
are not, and fsck can not fix the
inconsistencies, init drops the system
into single-user mode
for the system administrator to take care of the problems
directly.
Single-User Mode
This mode can be reached through the automatic reboot
sequence, or by the user booting with the
-s or setting the
boot_single variable in
loader .
It can also be reached by calling
shutdown without the reboot
(-r ) or halt (-h ) options,
from multi-user
mode.
If the system console console is set
to insecure in
/etc/ttys , then the system prompts for
the root password before initiating single-user mode.
An insecure console in /etc/ttys
# name getty type status comments
#
# This entry needed for asking password when init goes to single-user mode
# If you want to be asked for password, change "secure" to "insecure" here
console none unknown off insecure
An insecure console means that you
consider your physical security to the console to be
insecure, and want to make sure only someone who knows the
root password may use single-user mode, and it does not
mean that you want to run your console insecurely. Thus,
if you want security, choose insecure ,
not secure .
Multi-User Mode
If init finds your filesystems to be
in order, or once the user has finished in single-user mode, the
system enters multi-user mode, in which it starts the
resource configuration of the system.
Resource Configuration (rc)
The resource configuration system reads in
configuration defaults from
/etc/defaults/rc.conf , and
system-specific details from
/etc/rc.conf , and then proceeds to
mount the system filesystems mentioned in
/etc/fstab , start up networking
services, starts up miscellaneous system daemons, and
finally runs the startup scripts of locally installed
packages.
&man.rc.8; is a good reference to the resource
configuration system, as is examining the scripts
themselves.
Shutdown Sequence
Upon controlled shutdown, via shutdown ,
init will attempt to run the script
/etc/rc.shutdown , and then proceed to send
all processes the terminate signal, and subsequently the kill
signal to any that don't terminate timely.
diff --git a/en_US.ISO8859-1/books/handbook/disks/chapter.sgml b/en_US.ISO8859-1/books/handbook/disks/chapter.sgml
index 646638773c..469876183c 100644
--- a/en_US.ISO8859-1/books/handbook/disks/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/disks/chapter.sgml
@@ -1,1073 +1,1073 @@
Disks
Synopsis
This chapter covers how to use disks, whether physical,
memory, or networked, on FreeBSD.
BIOS Drive Numbering
Before you install and configure FreeBSD on your system, there is an
important subject that you should be aware of if, especially if you have
multiple hard drives.
In a PC running DOS or any of the BIOS-dependent operating systems
(WINxxx), the BIOS is able to abstract the normal disk drive order, and
the operating system goes along with the change. This allows the user
to boot from a disk drive other than the so-called primary
master
. This is especially convenient for some users who have
found that the simplest and cheapest way to keep a system backup is to
buy an identical second hard drive, and perform routine copies of the
first drive to the second drive using Ghost or XCOPY. Then, if the
first drive fails, or is attacked by a virus, or is scribbled upon by an
operating system defect, he can easily recover by instructing the BIOS
to logically swap the drives. It's like switching the cables on the
drives, but without having to open the case.
More expensive systems with SCSI controllers often include BIOS
extensions which allow the SCSI drives to be re-ordered in a similar
fashion for up to seven drives.
A user who is accustomed to taking advantage of these features may
become surprised when the results with FreeBSD are not as expected.
FreeBSD does not use the BIOS, and does not know the logical BIOS
drive mapping
. This can lead to very perplexing situations,
especially when drives are physically identical in geometry, and have
also been made as data clones of one another.
When using FreeBSD, always restore the BIOS to natural drive
numbering before installing FreeBSD, and then leave it that way. If you
need to switch drives around, then do so, but do it the hard way, and
open the case and move the jumpers and cables.
An illustration from the files of Bill and Fred's Exceptional
Adventures:
Bill breaks-down an older Wintel box to make another FreeBSD box
for Fred. Bill installs a single SCSI drive as SCSI unit zero, and
installs FreeBSD on it.
Fred begins using the system, but after several days notices that
the older SCSI drive is reporting numerous soft errors, and reports
this fact to Bill.
After several more days, Bill decides it's time to address the
situation, so he grabs an identical SCSI drive from the disk drive
"archive" in the back room. An initial surface scan indicates that
this drive is functioning well, so Bill installs this drive as SCSI
unit four, and makes an image copy from drive zero to drive four. Now
that the new drive is installed and functioning nicely, Bill decides
that it's a good idea to start using it, so he uses features in the
SCSI BIOS to re-order the disk drives so that the system boots from
SCSI unit four. FreeBSD boots and runs just fine.
Fred continues his work for several days, and soon Bill and Fred
decide that it's time for a new adventure -- time to upgrade to a
newer version of FreeBSD. Bill removes SCSI unit zero because it was
a bit flaky, and replaces it with another identical disk drive from
the "archive." Bill then installs the new version of FreeBSD onto the
new SCSI unit zero using Fred's magic Internet FTP floppies. The
installation goes well.
Fred uses the new version of FreeBSD for a few days, and certifies
that it is good enough for use in the engineering department...it's
time to copy all of his work from the old version. So Fred mounts
SCSI unit four (the latest copy of the older FreeBSD version). Fred
is dismayed to find that none of his precious work is present on SCSI
unit four.
Where did the data go?
When Bill made an image copy of the original SCSI unit zero onto
SCSI unit four, unit four became the "new clone," When Bill
re-ordered the SCSI BIOS so that he could boot from SCSI unit four, he
was only fooling himself. FreeBSD was still running on SCSI unit zero.
Making this kind of BIOS change will cause some or all of the Boot and
Loader code to be fetched from the selected BIOS drive, but when the
FreeBSD kernel drivers take-over, the BIOS drive numbering will be
ignored, and FreeBSD will transition back to normal drive numbering.
In the illustration at hand, the system continued to operate on the
original SCSI unit zero, and all of Fred's data was there, not on SCSI
unit four. The fact that the system appeared to be running on SCSI
unit four was simply an artifact of human expectations.
We are delighted to mention that no data bytes were killed or
harmed in any way by our discovery of this phenomenon. The older SCSI
unit zero was retrieved from the bone pile, and all of Fred's work was
returned to him, (and now Bill knows that he can count as high as
zero).
Although SCSI drives were used in this illustration, the concepts
apply equally to IDE drives.
Disk Naming
Physical drives come in two main flavors,
IDE , or SCSI ; but there
are also drives backed by RAID controllers, flash memory, and so
forth. Since these behave quite differently, they have their
own drivers and devices.
Physical Disk Naming Conventions
Drive type
Drive device name
IDE hard drives
ad in 4.0-RELEASE,
wd before 4.0-RELEASE.
IDE CDROM drives
acd from 3.1-RELEASE,
wcd before 4.0-RELEASE.
SCSI hard drives
da from 3.0-RELEASE,
sd before 3.0-RELEASE.
SCSI CDROM drives
cd
Assorted non-standard CDROM drives
mcd for Mitsumi CD-ROM,
scd for Sony CD-ROM,
matcd for Matsushita/Panasonic CD-ROM
Floppy drives
fd
SCSI tape drives
sa from 3.0-RELEASE,
st before 3.0-RELEASE.
IDE tape drives
ast from 4.0-RELEASE,
wst before 4.0-RELEASE.
Flash drives
fla for DiskOnChip Flash device
from 3.3-RELEASE.
RAID drives
myxd for Mylex, and
amrd for AMI MegaRAID,
idad for Compaq Smart RAID.
from 4.0-RELEASE. id between
3.2-RELEASE and 4.0-RELEASE.
Slices and Partitions
Physical disks usually contain
slices , unless they are
dangerously dedicated
. Slice numbers follow
the device name, prefixed with an s :
da0s1
.
Slices, dangerously dedicated
physical
drives, and other drives contain
partitions , which represented as
letters from a to h .
b is reserved for swap partitions, and
c is an unused partition the size of the
entire slice or drive. This is explained in .
Mounting and Unmounting Filesystems
The filesystem is best visualized as a tree,
rooted, as it were, at / .
/dev , /usr , and the
other directories in the root directory are branches, which may
have their own branches, such as
/usr/local , and so on.
There are various reasons to house some of these
directories on separate filesystems. /var
contains log, spool, and various types of temporary files, and
as such, may get filled up. Filling up the root filesystem
isn't a good idea, so splitting /var from
/ is often a good idea.
Another common reason to contain certain directory trees on
other filesystems is if they are to be housed on separate
physical disks, or are separate virtual disks, such as Network File System mounts, or CDROM
drives.
The fstab File
During the boot process,
filesystems listed in /etc/fstab are
automatically mounted (unless they are listed with
noauto ).
The /etc/fstab file contains a list
of lines of the following format:
device /mount-point fstype options dumpfreq passno
device is a device name (which should
exist), as explained in the Disk
naming conventions above.
mount-point is a directory (which
should exist), on which to mount the filesystem.
fstype is the filesystem type to pass
to &man.mount.8;. The default FreeBSD filesystem is
ufs .
options is either rw
for read-write filesystems, or ro for
read-only filesystems, followed by any other options that may
be needed. A common option is noauto for
filesystems not normally mounted during the boot sequence.
Other options in the &man.mount.8; manual page.
dumpfreq is the number of days the
filesystem should be dumped, and passno is
the pass number during which the filesystem is mounted during
the boot sequence.
The mount Command
The &man.mount.8; command is what is ultimately used to
mount filesystems.
In its most basic form, you use:
&prompt.root; mount device mountpoint
There are plenty of options, as mentioned in the
&man.mount.8; manual page, but the most common are:
mount options
-a
Mount all filesystems in
/etc/fstab , as modified by
-t , if given.
-d
Do everything but actually mount the
filesystem.
-f
Force the mounting the filesystem.
-r
Mount the filesystem read-only.
-t
fstype
Mount the given filesystem as the given filesystem
type, or mount only filesystems of the given type, if
given the -a option.
ufs
is the default filesystem
type.
-u
Update mount options on the filesystem.
-v
Be verbose.
-w
Mount the filesystem read-write.
The -o takes a comma-separated list of
the options, including the following:
nodev
Do not interpret special devices on the
filesystem. Useful security option.
noexec
Do not allow execution of binaries on this
filesystem. Useful security option.
nosuid
Do not interpret setuid or setgid flags on the
filesystem. Useful security option.
The umount Command
The umount command takes, as a parameter, one of a
mountpoint, a device name, or the -a or
-A option.
All forms take -f to force unmounting,
and -v for verbosity.
-a and -A are used to
unmount all mounted filesystems, possibly modified by the
filesystem types listed after -t .
-A , however, doesn't attempt to unmount the
root filesystem.
Adding Disks
Originally contributed by &a.obrien; 26 April
1998
Lets say we want to add a new SCSI disk to a machine that currently
only has a single drive. First turn off the computer and install the
drive in the computer following the instructions of the computer,
controller, and drive manufacturer. Due the wide variations of procedures
to do this, the details are beyond the scope of this document.
Login as user root . After you've installed the
drive, inspect /var/run/dmesg.boot to ensure the new
disk was found. Continuing with our example, the newly added drive will
be da1 and we want to mount it on
/1 (if you are adding an IDE drive, it will
be wd1 in pre-4.0 systems, or
ad1 in most 4.X systems).
Because FreeBSD runs on IBM-PC compatible computers, it must take into
account the PC BIOS partitions. These are different from the traditional
BSD partitions. A PC disk has up to four BIOS partition entries. If the
disk is going to be truly dedicated to FreeBSD, you can use the
dedicated mode. Otherwise, FreeBSD will have to live
with in one of the PC BIOS partitions. FreeBSD calls the PC BIOS
partitions slices so as not to confuse them with
traditional BSD partitions. You may also use slices on a disk that is
dedicated to FreeBSD, but used in a computer that also has another
operating system installed. This is to not confuse the
fdisk utility of the other operating system.
In the slice case the drive will be added as
/dev/da1s1e . This is read as: SCSI disk, unit number
1 (second SCSI disk), slice 1 (PC BIOS partition 1), and
e BSD partition. In the dedicated case, the drive
will be added simply as /dev/da1e .
Using sysinstall
You may use /stand/sysinstall to partition and
label a new disk using its easy to use menus. Either login as user
root or use the su command. Run
/stand/sysinstall and enter the
Configure menu. With in the FreeBSD
Configuration Menu , scroll down and select the
Partition item. Next you should be presented with a
list of hard drives installed in your system. If you do not see
da1 listed, you need to recheck your physical
installation and dmesg output in the file
/var/run/dmesg.boot .
Select da1 to enter the FDISK Partition
Editor . Choose A to use the entire disk
for FreeBSD. When asked if you want to remain cooperative with
any future possible operating systems
, answer
YES . Write the changes to the disk using
W . Now exit the FDISK editor using
q . Next you will be asked about the Master Boot
Record. Since you are adding a disk to an already running system,
choose None .
Next enter the Disk Label Editor . This is where
you will create the traditional BSD partitions. A disk can have up to
eight partitions, labeled a-h. A few of the partition labels have
special uses. The a partition is used for the root
partition (/ ). Thus only your system disk (e.g,
the disk you boot from) should have an a partition.
The b partition is used for swap partitions, and you
may have many disks with swap partitions. The c
partition addresses the entire disk in dedicated mode, or the entire
FreeBSD slice in slice mode. The other partitions are for general
use.
Sysinstall's Label editor favors the e partition
for non-root, non-swap partitions. With in the Label editor, create a
single file system using C . When prompted if this
will be a FS (file system) or swap, choose FS and
give a mount point (e.g, /mnt ). When adding a disk
in post-install mode, Sysinstall will not create entries in
/etc/fstab for you, so the mount point you specify
isn't important.
You are now ready to write the new label to the disk and create a
file system on it. Do this by hitting W . Ignore any
errors from Sysinstall that it could not mount the new partition. Exit
the Label Editor and Sysinstall completely.
The last step is to edit /etc/fstab to add an
entry for your new disk.
Using Command Line Utilities
Using Slices
This setup will allow your disk to work correctly with
other operating systems that might be installed on your
computer and will not confuse other operating systems' fdisk
utilities. It is recommended to use this method for new disk
installs. Only use dedicated mode if you
have a good reason to do so!
&prompt.root; dd if=/dev/zero of=/dev/rda1 bs=1k count=1
&prompt.root; fdisk -BI da1 #Initialize your new disk
&prompt.root; disklabel -B -w -r da1s1 auto #Label it.
&prompt.root; disklabel -e da1s1 # Now edit the disklabel you just created and add any partitions.
&prompt.root; mkdir -p /1
&prompt.root; newfs /dev/da1s1e # Repeat this for every partition you created.
&prompt.root; mount -t ufs /dev/da1s1e /1 # Mount the partition(s)
&prompt.root; vi /etc/fstab # When satisfied, add the appropriate entry/entries to your /etc/fstab .
If you have an IDE disk, substitute ad
for da . On pre-4.x systems use
wd .
Dedicated
If you will not be sharing the new drive with another operating
system, you may use the dedicated mode. Remember
this mode can confuse Microsoft operating systems; however, no damage
will be done by them. IBM's OS/2 however, will
appropriate
any partition it finds which it doesn't
understand.
&prompt.root; dd if=/dev/zero of=/dev/rda1 bs=1k count=1
&prompt.root; disklabel -Brw da1 auto
&prompt.root; disklabel -e da1 # create the `e' partition
&prompt.root; newfs -d0 /dev/rda1e
&prompt.root; mkdir -p /1
&prompt.root; vi /etc/fstab # add an entry for /dev/da1e
&prompt.root; mount /1
An alternate method is:
&prompt.root; dd if=/dev/zero of=/dev/rda1 count=2
&prompt.root; disklabel /dev/rda1 | disklabel -BrR da1 /dev/stdin
&prompt.root; newfs /dev/rda1e
&prompt.root; mkdir -p /1
&prompt.root; vi /etc/fstab # add an entry for /dev/da1e
&prompt.root; mount /1
Virtual Disks: Network, Memory, and File-Based Filesystems
Aside from the disks you physically insert into your computer:
floppies, CDs, hard drives, and so forth; other forms of disks
are understood by FreeBSD - the virtual
disks .
These include network filesystems such as the Network Filesystem and Coda, memory-based
filesystems such as md and
file-backed filesystems created by vnconfig.
vnconfig: file-backed filesystem
&man.vnconfig.8; configures and enables vnode pseudo disk
devices. A vnode is a representation
of a file, and is the focus of file activity. This means that
&man.vnconfig.8; uses files to create and operate a
filesystem. One possible use is the mounting of floppy or CD
images kept in files.
To mount an existing filesystem image:
Using vnconfig to mount an existing filesystem
image
&prompt.root; vnconfig vn0 diskimage
&prompt.root; mount /dev/vn0 c /mnt
To create a new filesystem image with vnconfig:
Creating a New File-Backed Disk with vnconfig
&prompt.root; dd if=/dev/zero of=newimage bs=1k count=5 k
5120+0 records in
5120+0 records out
&prompt.root; vnconfig -s labels -c vn0 newimage
&prompt.root; disklabel -r -w vn0 auto
&prompt.root; newfs vn0 c
Warning: 2048 sector(s) in last cylinder unallocated
/dev/rvn0c: 10240 sectors in 3 cylinders of 1 tracks, 4096 sectors
5.0MB in 1 cyl groups (16 c/g, 32.00MB/g, 1280 i/g)
super-block backups (for fsck -b #) at:
32
&prompt.root; mount /dev/vn0 c /mnt
&prompt.root; df /mnt
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/vn0c 4927 1 4532 0% /mnt
md: Memory Filesystem
md is a simple, efficient means to do memory
filesystems.
Simply take a filesystem you've prepared with, for
example, &man.vnconfig.8;, and:
md memory disk
&prompt.root; dd if=newimage of=/dev/md0
5120+0 records in
5120+0 records out
&prompt.root; mount /dev/md0c /mnt
&prompt.root; df /mnt
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/md0c 4927 1 4532 0% /mnt
Disk Quotas
Quotas are an optional feature of the operating system that
allow you to limit the amount of disk space and/or the number of
files a user, or members of a group, may allocate on a per-file
system basis. This is used most often on timesharing systems where
it is desirable to limit the amount of resources any one user or
group of users may allocate. This will prevent one user from
consuming all of the available disk space.
Configuring Your System to Enable Disk Quotas
Before attempting to use disk quotas it is necessary to make
sure that quotas are configured in your kernel. This is done by
adding the following line to your kernel configuration
file:
options QUOTA
The stock GENERIC kernel does not have
this enabled by default, so you will have to configure, build and
install a custom kernel in order to use disk quotas. Please refer
to the Configuring the FreeBSD
Kernel section for more information on kernel
configuration.
Next you will need to enable disk quotas in
/etc/rc.conf . This is done by adding the
line:
enable_quotas=YES
For finer control over your quota startup, there is an
additional configuration variable available. Normally on bootup,
the quota integrity of each file system is checked by the
quotacheck program. The
quotacheck facility insures that the data in
the quota database properly reflects the data on the file system.
This is a very time consuming process that will significantly
affect the time your system takes to boot. If you would like to
skip this step, a variable is made available for the
purpose:
check_quotas=NO
If you are running FreeBSD prior to 3.2-RELEASE, the
configuration is simpler, and consists of only one variable. Set
the following in your /etc/rc.conf :
check_quotas=YES
Finally you will need to edit /etc/fstab
to enable disk quotas on a per-file system basis. This is where
you can either enable user or group quotas or both for all of your
file systems.
To enable per-user quotas on a file system, add the
userquota option to the options field in the
/etc/fstab entry for the file system you want
- to to enable quotas on. For example:
+ to enable quotas on. For example:
/dev/da1s2g /home ufs rw,userquota 1 2
Similarly, to enable group quotas, use the
groupquota option instead of the
userquota keyword. To enable both user and
group quotas, change the entry as follows:
/dev/da1s2g /home ufs rw,userquota,groupquota 1 2
By default the quota files are stored in the root directory of
the file system with the names quota.user and
quota.group for user and group quotas
respectively. See man fstab for more
information. Even though that man page says that you can specify
an alternate location for the quota files, this is not recommended
because the various quota utilities do not seem to handle this
properly.
At this point you should reboot your system with your new
kernel. /etc/rc will automatically run the
appropriate commands to create the initial quota files for all of
the quotas you enabled in /etc/fstab , so
there is no need to manually create any zero length quota
files.
In the normal course of operations you should not be required
to run the quotacheck ,
quotaon , or quotaoff
commands manually. However, you may want to read their man pages
just to be familiar with their operation.
Setting Quota Limits
Once you have configured your system to enable quotas, verify
that they really are enabled. An easy way to do this is to
run:
&prompt.root; quota -v
You should see a one line summary of disk usage and current
quota limits for each file system that quotas are enabled
on.
You are now ready to start assigning quota limits with the
edquota command.
You have several options on how to enforce limits on the
amount of disk space a user or group may allocate, and how many
files they may create. You may limit allocations based on disk
space (block quotas) or number of files (inode quotas) or a
combination of both. Each of these limits are further broken down
into two categories; hard and soft limits.
A hard limit may not be exceeded. Once a user reaches his
hard limit he may not make any further allocations on the file
system in question. For example, if the user has a hard limit of
500 blocks on a file system and is currently using 490 blocks, the
user can only allocate an additional 10 blocks. Attempting to
allocate an additional 11 blocks will fail.
Soft limits, on the other hand, can be exceeded for a limited
amount of time. This period of time is known as the grace period,
which is one week by default. If a user stays over his or her
soft limit longer than the grace period, the soft limit will
turn into a hard limit and no further allocations will be allowed.
When the user drops back below the soft limit, the grace period
will be reset.
The following is an example of what you might see when you run
the edquota command. When the
edquota command is invoked, you are placed into
the editor specified by the EDITOR environment
variable, or in the vi editor if the
EDITOR variable is not set, to allow you to edit
the quota limits.
&prompt.root; edquota -u test
Quotas for user test:
/usr: blocks in use: 65, limits (soft = 50, hard = 75)
inodes in use: 7, limits (soft = 50, hard = 60)
/usr/var: blocks in use: 0, limits (soft = 50, hard = 75)
inodes in use: 0, limits (soft = 50, hard = 60)
You will normally see two lines for each file system that has
quotas enabled. One line for the block limits, and one line for
inode limits. Simply change the value you want updated to modify
the quota limit. For example, to raise this users block limit
from a soft limit of 50 and a hard limit of 75 to a soft limit of
500 and a hard limit of 600, change:
/usr: blocks in use: 65, limits (soft = 50, hard = 75)
to:
/usr: blocks in use: 65, limits (soft = 500, hard = 600)
The new quota limits will be in place when you exit the
editor.
Sometimes it is desirable to set quota limits on a range of
uids. This can be done by use of the -p option
on the edquota command. First, assign the
desired quota limit to a user, and then run
edquota -p protouser startuid-enduid . For
example, if user test has the desired quota
limits, the following command can be used to duplicate those quota
limits for uids 10,000 through 19,999:
&prompt.root; edquota -p test 10000-19999
See man edquota for more detailed
information.
Checking Quota Limits and Disk Usage
You can use either the quota or the
repquota commands to check quota limits and
disk usage. The quota command can be used to
check individual user and group quotas and disk usage. Only the
super-user may examine quotas and usage for other users, or for
groups that they are not a member of. The
repquota command can be used to get a summary
of all quotas and disk usage for file systems with quotas
enabled.
The following is some sample output from the
quota -v command for a user that has quota
limits on two file systems.
Disk quotas for user test (uid 1002):
Filesystem blocks quota limit grace files quota limit grace
/usr 65* 50 75 5days 7 50 60
/usr/var 0 50 75 0 50 60
On the /usr file system in the above
example this user is currently 15 blocks over the soft limit of
50 blocks and has 5 days of the grace period left. Note the
asterisk * which indicates that the user is
currently over his quota limit.
Normally file systems that the user is not using any disk
space on will not show up in the output from the
quota command, even if he has a quota limit
assigned for that file system. The -v option
will display those file systems, such as the
/usr/var file system in the above
example.
Quotas over NFS
Quotas are enforced by the quota subsystem on the NFS server.
The &man.rpc.rquotad.8; daemon makes quota information available
to the &man.quota.1; command on NFS clients, allowing users on
those machines to see their quota statistics.
Enable rpc.rquotad in
/etc/inetd.conf like so:
rquotad/1 dgram rpc/udp wait root /usr/libexec/rpc.rquotad rpc.rquotad
Now restart inetd :
&prompt.root; kill -HUP `cat /var/run/inetd.pid`
Creating CDs
Contributed by Mike Meyer
mwm@mired.org , April 2001.
Introduction
CDs have a number of features that differentiate them from
conventional disks. Initially, they weren't writable by the
user. They are designed so that they can be read continuously without
delays to move the head between tracks. They are also much easier
to transport between systems than similarly sized media were at the
time.
CDs do have tracks, but this refers to a section of data to
be read continuously and not a physical property of the disk. To
produce a CD on FreeBSD, you prepare the data files that are going
to make up the tracks on the CD, then write the tracks to the
CD.
The ISO 9660 file system was designed to deal with these
differences. It unfortunately codifies file system limits that were
common then. Fortunately, it provides an extension mechanism that
allows properly written CDs to exceed those limits while still
working with systems that do not support those extensions.
The mkisofs
program is used to produce a data file containing an ISO 9660 file
system. It has options that support various extensions, and is
described below. You can install it with the
/usr/ports/sysutils/mkisofs port.
Which tool to use to burn the CD depends on whether your CD burner
is ATAPI or something else. ATAPI CD burners use the burncd program that is part of
the base system. SCSI and USB CD burners should use the
cdrecord from
the /usr/ports/sysutils/cdrecord port.
mkisofs
mkisofs produces an ISO 9660 file system
that is an image of a directory tree in the Unix file system name
space. The simplest usage is:
&prompt.root; mkisofs -o imagefile.iso /path/to/tree
This command will create an imagefile
containing an ISO 9660 file system that is a copy of the tree at
/path/to/tree . In the process, it will
map the file names to names that fit the limitations of the
standard ISO 9660 file system, and will exclude files that have
names uncharacteristic of ISO file systems. Read &man.mkisofs.8;
for details of this process, and options that can be used to
control it.
A number of options are available to overcome those
restrictions. In particular, -R enables the
Rock Ridge extensions common to Unix systems, -J
enables Joliet extensions used by Microsoft systems, and
-hfs can be used to create HFS file systems used
by Macs. Read &man.mkisofs.8; for more information on the last
two.
For CDs that are going to be used only on FreeBSD systems,
-U can be used to disable all filename
restrictions. When used with -R , it produces a
file system image that is identical to the FreeBSD tree you started
from, though it may violate the ISO 9660 standard in a number of
ways.
The last option of general use is -b . This is
used to specify the location of the boot image for use in producing an
El Torito
bootable CD. This option takes an
argument which is the path to a boot image from the top of the
tree being written to the CD. So, given that
/tmp/myboot holds a bootable FreeBSD system
with the boot image in
/tmp/myboot/boot/cdboot , you could produce the
image of an ISO 9660 file system in
/tmp/bootable.iso like so:
&prompt.root; mkisofs -U -R -b boot/cdboot -o /tmp/bootable.iso /tmp/myboot
Having done that, if you have vn configured in your kernel, you
can mount the file system with:
&prompt.root; vnconfig -e vn0c /tmp/bootable.iso
&prompt.root; mount -t cd9660 /dev/vn0c /mnt
At which point you can verify that /mnt
and /tmp/myboot are identical.
There are many other options you can use with
mkisofs to fine-tune its behavior. See
&man.mkisofs.8; for details.
burncd
If you have an ATAPI CD burner, you can use the
burncd command to burn an ISO image onto a
CD. burncd is part of the base system, installed
as /usr/sbin/burncd . Usage is very simple, as
it has few options:
&prompt.root; burncd -f cddevice data imagefile.iso fixate
Will burn a copy of imagefile.iso on
cddevice . The default device is
/dev/acd0 . See &man.burncd.8; for options to
set the write speed, eject the CD after burning, and write audio
data.
cdrecord
If you do not have an ATAPI CD burner, you will have to use
cdrecord to burn your
CDs. cdrecord is not part of the base system;
you must install it from either the port at
/usr/ports/sysutils/cdrecord or the appropriate
package. Changes to the base system can cause binary versions of
this program to fail, possibly resulting in a
coaster
. You should therefore either upgrade the
port when you upgrade your system, or if you are tracking -stable, upgrade the port when a
new version becomes available.
While cdrecord has many options, basic usage
is even simpler than burncd . Burning an ISO 9660
image is done with:
&prompt.root; cdrecord dev= device imagefile.iso
The tricky part of using cdrecord is finding
the dev to use. To find the proper setting, use
the -scanbus flag of cdrecord ,
which might produce results like this:
&prompt.root; cdrecord -scanbus
Cdrecord 1.9 (i386-unknown-freebsd4.2) Copyright (C) 1995-2000 Jörg Schilling
Using libscg version 'schily-0.1'
scsibus0:
0,0,0 0) 'SEAGATE ' 'ST39236LW ' '0004' Disk
0,1,0 1) 'SEAGATE ' 'ST39173W ' '5958' Disk
0,2,0 2) *
0,3,0 3) 'iomega ' 'jaz 1GB ' 'J.86' Removable Disk
0,4,0 4) 'NEC ' 'CD-ROM DRIVE:466' '1.26' Removable CD-ROM
0,5,0 5) *
0,6,0 6) *
0,7,0 7) *
scsibus1:
1,0,0 100) *
1,1,0 101) *
1,2,0 102) *
1,3,0 103) *
1,4,0 104) *
1,5,0 105) 'YAMAHA ' 'CRW4260 ' '1.0q' Removable CD-ROM
1,6,0 106) 'ARTEC ' 'AM12S ' '1.06' Scanner
1,7,0 107) *
This lists the appropriate dev value for the
devices on the list. Locate your CD burner, and use the three
numbers separated by commas as the value for
dev . In this case, the CRW device is 1,5,0, so the
appropriate input would be
dev =1,5,0 . There are easier
ways to specify this value; see &man.cdrecord.1; for
details. That is also the place to look for information on writing
audio tracks, controlling the speed, and other things.
diff --git a/en_US.ISO8859-1/books/handbook/hw/chapter.sgml b/en_US.ISO8859-1/books/handbook/hw/chapter.sgml
index 79502f53d7..2898faf3d7 100644
--- a/en_US.ISO8859-1/books/handbook/hw/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/hw/chapter.sgml
@@ -1,5854 +1,5854 @@
PC Hardware compatibility
Issues of hardware compatibility are among the most troublesome in the
computer industry today and FreeBSD is by no means immune to trouble. In
this respect, FreeBSD's advantage of being able to run on inexpensive
commodity PC hardware is also its liability when it comes to support for
the amazing variety of components on the market. While it would be
impossible to provide an exhaustive listing of hardware that FreeBSD
supports, this section serves as a catalog of the device drivers included
with FreeBSD and the hardware each drivers supports. Where possible and
appropriate, notes about specific products are included. You may also
want to refer to the kernel
configuration file section in this handbook for a list of
supported devices.
As FreeBSD is a volunteer project without a funded testing department,
we depend on you, the user, for much of the information contained in this
catalog. If you have direct experience of hardware that does or does not
work with FreeBSD, please let us know by sending e-mail to the &a.doc;.
Questions about supported hardware should be directed to the &a.questions;
(see Mailing Lists for more
information). When submitting information or asking a question, please
remember to specify exactly what version of FreeBSD you are using and
include as many details of your hardware as possible.
Resources on the Internet
The following links have proven useful in selecting hardware. Though
some of what you see won't necessarily be specific (or even applicable)
to FreeBSD, most of the hardware information out there is OS
independent. Please check with the FreeBSD hardware guide to make sure
that your chosen configuration is supported before making any
purchases.
The Pentium Systems
Hardware Performance Guide
Sample Configurations
The following list of sample hardware configurations by no means
constitutes an endorsement of a given hardware vendor or product by
The FreeBSD Project . This information is provided
only as a public service and merely catalogs some of the experiences
that various individuals have had with different hardware combinations.
Your mileage may vary. Slippery when wet. Beware of dog.
Jordan's Picks
I have had fairly good luck building workstation and server
configurations with the following components. I can't guarantee that
you will too, nor that any of the companies here will remain
best buys
forever. I will try, when I can, to keep this
list up-to-date but cannot obviously guarantee that it will be at any
given time.
Motherboards
For Pentium Pro (P6) systems, I'm quite fond of the Tyan S1668
dual-processor motherboard as well as the Intel PR440FX motherboard
with on-board SCSI WIDE and 100/10MB Intel EtherExpress NIC. You
can build a dandy little single or dual processor system (which is
supported in FreeBSD 3.0) for very little cost now that the Pentium
Pro 180/256K chips have fallen so greatly in price, but no telling
how much longer this will last.
For the Pentium II, I'm rather partial to the ASUS P2l97-S
motherboard with the on-board Adaptec SCSI WIDE controller.
For Pentium machines, the ASUS P55T2P4
motherboard appears to be a good choice for mid-to-high range
Pentium server and workstation systems.
Those wishing to build more fault-tolerant systems should also
be sure to use Parity memory or, for truly 24/7 applications, ECC
memory.
ECC memory does involve a slight performance trade-off (which
may or may not be noticeable depending on your application) but
buys you significantly increased fault-tolerance to memory
errors.
Disk Controllers
This one is a bit trickier, and while I used to recommend the
Buslogic controllers
unilaterally for everything from ISA to PCI, now I tend to lean
towards the Adaptec
1542CF for ISA, Buslogic Bt747c for EISA and Adaptec 2940UW for
PCI.
The NCR/Symbios cards for PCI have also worked well for me,
though you need to make sure that your motherboard supports the
BIOS-less model if you're using one of those (if your card has
nothing which looks even vaguely like a ROM chip on it, you've
probably got one which expects its BIOS to be on your
motherboard).
If you should find that you need more than one SCSI controller
in a PCI machine, you may wish to consider conserving your scarce
PCI bus resources by buying the Adaptec 3940 card, which puts two
SCSI controllers (and internal busses) in a single slot.
There are two types of 3940 on the market—the older
model with AIC 7880 chips on it, and the newer one with AIC 7895
chips. The newer model requires CAM
support which is not yet part of FreeBSD—you have to add it,
or install from one of the CAM binary snapshot release.
Disk drives
In this particular game of Russian roulette, I'll make few
specific recommendations except to say SCSI over IDE whenever
you can afford it.
Even in small desktop configurations, SCSI
often makes more sense since it allows you to easily migrate drives
from server to desktop as falling drive prices make it economical to
do so. If you have more than one machine to administer then think
of it not simply as storage, think of it as a food chain! For a
serious server configuration, there's not even any
argument—use SCSI equipment and good cables.
CDROM drives
My SCSI preferences extend to SCSI CDROM drives as well, and
while the Toshiba drives
have always been favorites of mine (in whatever speed is hot that
week), I'm still fond of my good old Plextor PX-12CS drive. It's
only a 12 speed, but it's offered excellent performance and
reliability.
Generally speaking, most SCSI CDROM drives I've seen have been
of pretty solid construction and you probably won't go wrong with an
HP or NEC SCSI CDROM drive either. SCSI CDROM prices also appear to
have dropped considerably in the last few months and are now quite
competitive with IDE CDROMs while remaining a technically superior
solution. I now see no reason whatsoever to settle for an IDE CDROM
drive if given a choice between the two.
CD Recordable (WORM) drives
At the time of this writing, FreeBSD supports 3 types of CDR
drives (though I believe they all ultimately come from Phillips
anyway): The Phillips CDD 522 (Acts like a Plasmon), the PLASMON
RF4100 and the HP 6020i. I myself use the HP 6020i for burning
CDROMs (in 2.2 and later releases—it does not work with
earlier releases of the SCSI code) and it works very well. See
/usr/share/examples/worm
on your 2.2 system for example scripts used to created ISO9660
filesystem images (with RockRidge extensions) and burn them onto an
HP6020i CDR.
Tape drives
I've had pretty good luck with both 8mm
drives from Exabyte and 4mm
(DAT) drives from HP .
For backup purposes, I'd have to give the higher recommendation
to the Exabyte due to the more robust nature (and higher storage
capacity) of 8mm tape.
Video Cards
If you can also afford to buy a commercial X server for
US$99 from Xi Graphics, Inc.
(formerly X Inside, Inc) then I can heartily recommend the
Matrox Millenium
II card. Note that support for this card is also
excellent with the XFree86 server, which is now
at version 3.3.2.
You also certainly can't go wrong with one of Number 9's cards — their
S3 Vision 868 and 968 based cards (the 9FX series) also being quite
fast and very well supported by XFree86's S3 server. You can also
pick up their Revolution 3D cards very cheaply these days,
especially if you require a lot of video memory.
Monitors
I have had very good luck with the Sony
Multiscan 17seII monitors , as have I with the Viewsonic
offering in the same (Trinitron) tube. For larger than 17", all I
can recommend at the time of this writing is to not spend any less
than U.S. $2,000 for a 21" monitor or $1,700 for a 20"
monitor if that's what you really need. There are good monitors
available in the >=20" range and there are also cheap monitors in
the >=20" range. Unfortunately, very few are both cheap and
good!
Networking
I can recommend the Intel EtherExpress Pro/100B card first and
foremost, followed by the SMC Ultra 16 controller for any
ISA application and the SMC EtherPower or Compex ENET32 cards for
slightly cheaper PCI based networking. In general, any PCI NIC
based around DEC's DC21041 Ethernet controller chip, such as the
Znyx ZX342 or DEC DE435/450, will generally work quite well and can
frequently be found in 2-port and 4-port version (useful for
firewalls and routers), though the Pro/100MB card has the edge when
it comes to providing the best performance with lower
overhead.
If what you're looking for is the cheapest possible solution
then almost any NE2000 clone will do a fine job for very little
cost.
Serial
If you're looking for high-speed serial networking solutions,
then Digi International
makes the SYNC/570
series, with drivers now in FreeBSD-CURRENT. Emerging Technologies also
manufactures a board with T1/E1 capabilities, using software they
provide. I have no direct experience using either product,
however.
multiport card options are somewhat more numerous, though it has
to be said that FreeBSD's support for Cyclades 's products is
probably the tightest, primarily as a result of that company's
commitment to making sure that we are adequately supplied with
evaluation boards and technical specs. I've heard that the
Cyclom-16Ye offers the best price/performance, though I've not
checked the prices lately. Other multiport cards I've heard good
things about are the BOCA and AST cards, and Stallion Technologies
apparently offers an unofficial driver for their cards at this
location.
Audio
I currently use a Creative
Labs AWE32 though just about anything from Creative Labs
will generally work these days. This is not to say that other types
of sound cards don't also work, simply that I have little experience
with them (I was a former GUS fan, but Gravis's sound card situation
has been dire for some time).
Video
For video capture, there are two good choices — any card
based on the Brooktree BT848 chip, such as the Hauppauge or WinTV
boards, will work very nicely with FreeBSD. Another board which
works for me is the Matrox Meteor card.
FreeBSD also supports the older video spigot card from Creative
Labs, but those are getting somewhat difficult to find. Note that
the Meteor frame grabber card will not work
with motherboards based on the 440FX chipset! See the motherboard reference section for details.
In such cases, it's better to go with a BT848 based board.
Core/Processing
Motherboards, busses, and chipsets
* ISA
* EISA
* VLB
PCI
Contributed by &a.obrien; from postings by
&a.rgrimes;. 25 April 1995.
Continuing updates by &a.jkh;. Last
update on 26 August 1996.
Of the Intel PCI chip sets, the following list describes various
types of known-brokenness and the degree of breakage, listed from
worst to best.
Mercury:
Cache coherency problems, especially if there are ISA bus
masters behind the ISA to PCI bridge chip. Hardware flaw, only
known work around is to turn the cache off.
Saturn-I (ie, 82424ZX at rev 0, 1 or
2) :
Write back cache coherency problems. Hardware flaw, only
known work around is to set the external cache to
write-through mode. Upgrade to Saturn-II.
Saturn-II (ie, 82424ZX at rev 3 or
4) :
Works fine, but many MB manufactures leave out the
external dirty bit SRAM needed for write back operation.
You can work around this either by running it in write
through mode, or get the dirty bit SRAM installed (I
have these for the ASUS PCI/I-486SP3G rev 1.6 and later
boards).
Neptune:
Can not run more than 2 bus master devices. Admitted Intel
design flaw. Workarounds include do not run more than 2 bus
masters, special hardware design to replace the PCI bus
arbiter (appears on Intel Altair board and several other Intel
server group MB's). And of course Intel's official answer,
move to the Triton chip set, we fixed it
there
.
Triton (ie, 430FX) :
No known cache coherency or bus master problems, chip set
does not implement parity checking. Workaround for parity
issue. Use Triton-II based motherboards if you have the
choice.
Triton-II (ie, 430HX) :
All reports on motherboards using this chipset have been
favorable so far. No known problems.
Orion:
Early versions of this chipset suffered from a PCI
write-posting bug which can cause noticeable performance
degradation in applications where large amounts of PCI bus
traffic is involved. B0 stepping or later revisions of the
chipset fixed this problem.
440FX :
This Pentium
Pro support chipset seems to work well, and does not
suffer from any of the early Orion chipset problems. It also
supports a wider variety of memory, including ECC and parity.
The only known problem with it is that the Matrox Meteor frame
grabber card doesn't like it.
CPUs/FPUs
Contributed by &a.asami;. 26 December
1997.
P6 class (Pentium Pro/Pentium II)
Both the Pentium Pro and Pentium II work fine with FreeBSD. In
fact, our main FTP site ftp.FreeBSD.org (also known
as "ftp.cdrom.com ", world's largest ftp site)
runs FreeBSD on a Pentium Pro. Configurations
details are available for interested parties.
Pentium class
The Intel Pentium (P54C), Pentium MMX (P55C), AMD K6 and
Cyrix/IBM 6x86MX processors are all reported to work with FreeBSD.
I will not go into details of which processor is faster than what,
there are millions of web sites on the Internet that tells you one
way or another. :)
Various CPUs have different voltage/cooling requirements. Make
sure your motherboard can supply the exact voltage needed by the
CPU. For instance, many recent MMX chips require split voltage
(e.g., 2.9V core, 3.3V I/O). Also, some AMD and Cyrix/IBM chips
run hotter than Intel chips. In that case, make sure you have
good heatsink/fans (you can get the list of certified parts from
their web pages).
Clock speeds
Contributed by &a.rgrimes;. 1 October
1996.
Updated by &a.asami;. 27 December
1997.
Pentium class machines use different clock speeds for the
various parts of the system. These being the speed of the CPU,
external memory bus, and the PCI bus. It is not always true that
a faster
processor will make a system faster than a
slower
one, due to the various clock speeds used.
Below is a table showing the differences:
Rated CPU MHz
External Clock and Memory Bus MHz
External to Internal Clock Multiplier
PCI Bus Clock MHz
60
60
1.0
30
66
66
1.0
33
75
50
1.5
25
90
60
1.5
30
100
50
2
25
100
66
1.5
33
120
60
2
30
133
66
2
33
150
60
2.5
30 (Intel, AMD)
150
75
2
37.5 (Cyrix/IBM 6x86MX)
166
66
2.5
33
180
60
3
30
200
66
3
33
233
66
3.5
33
66MHz may actually be 66.667MHz, but don't assume so.
The Pentium 100 can be run at either 50MHz external clock
with a multiplier of 2 or at 66MHz and a multiplier of
1.5.
As can be seen the best parts to be using are the 100, 133,
166, 200 and 233, with the exception that at a multiplier of 3 or
more the CPU starves for memory.
The AMD K6 Bug
In 1997, there have been reports of the AMD K6 seg faulting
during heavy compilation. That problem has been fixed in 3Q '97.
According to reports, K6 chips with date mark 9733
or larger (i.e., manufactured in the 33rd week of '97 or later) do
not have this bug.
* 486 class
* 386 class
286 class
Sorry, FreeBSD does not run on 80286 machines. It is nearly
impossible to run today's large full-featured unices on such
hardware.
* Memory
The minimum amount of memory you must have to install FreeBSD is 5
MB. Once your system is up and running you can build a custom kernel that
will use less memory. If you use the boot4.flp
you can get away with having only 4 MB.
* BIOS
Input/Output Devices
* Video cards
* Sound cards
Serial ports and multiport cards
The UART: What it is and how it works
Copyright © 1996 &a.uhclem;, All Rights
Reserved. 13 January 1996.
The Universal Asynchronous Receiver/Transmitter (UART)
controller is the key component of the serial communications
subsystem of a computer. The UART takes bytes of data and transmits
the individual bits in a sequential fashion. At the destination, a
second UART re-assembles the bits into complete bytes.
Serial transmission is commonly used with modems and for
non-networked communication between computers, terminals and other
devices.
There are two primary forms of serial transmission: Synchronous
and Asynchronous. Depending on the modes that are supported by the
hardware, the name of the communication sub-system will usually
include a A if it supports Asynchronous
communications, and a S if it supports
Synchronous communications. Both forms are described below.
Some common acronyms are:
UART Universal Asynchronous
Receiver/Transmitter
USART Universal Synchronous-Asynchronous
Receiver/Transmitter
Synchronous Serial Transmission
Synchronous serial transmission requires that the sender and
receiver share a clock with one another, or that the sender
provide a strobe or other timing signal so that the receiver knows
when to read
the next bit of the data. In most
forms of serial Synchronous communication, if there is no data
available at a given instant to transmit, a fill character must be
sent instead so that data is always being transmitted.
Synchronous communication is usually more efficient because only
data bits are transmitted between sender and receiver, and
- synchronous communication can be more more costly if extra wiring
+ synchronous communication can be more costly if extra wiring
and circuits are required to share a clock signal between the
sender and receiver.
A form of Synchronous transmission is used with printers and
fixed disk devices in that the data is sent on one set of wires
while a clock or strobe is sent on a different wire. Printers and
fixed disk devices are not normally serial devices because most
fixed disk interface standards send an entire word of data for
each clock or strobe signal by using a separate wire for each bit
of the word. In the PC industry, these are known as Parallel
devices.
The standard serial communications hardware in the PC does not
support Synchronous operations. This mode is described here for
comparison purposes only.
Asynchronous Serial Transmission
Asynchronous transmission allows data to be transmitted
without the sender having to send a clock signal to the receiver.
Instead, the sender and receiver must agree on timing parameters
in advance and special bits are added to each word which are used
to synchronize the sending and receiving units.
When a word is given to the UART for Asynchronous
transmissions, a bit called the "Start Bit" is added to the
beginning of each word that is to be transmitted. The Start Bit
is used to alert the receiver that a word of data is about to be
sent, and to force the clock in the receiver into synchronization
with the clock in the transmitter. These two clocks must be
accurate enough to not have the frequency drift by more than 10%
during the transmission of the remaining bits in the word. (This
requirement was set in the days of mechanical teleprinters and is
easily met by modern electronic equipment.)
After the Start Bit, the individual bits of the word of data
are sent, with the Least Significant Bit (LSB) being sent first.
Each bit in the transmission is transmitted for exactly the same
amount of time as all of the other bits, and the receiver
looks
at the wire at approximately halfway through
the period assigned to each bit to determine if the bit is a
1 or a 0 . For example, if
it takes two seconds to send each bit, the receiver will examine
the signal to determine if it is a 1 or a
0 after one second has passed, then it will
wait two seconds and then examine the value of the next bit, and
so on.
The sender does not know when the receiver has
looked
at the value of the bit. The sender only
knows when the clock says to begin transmitting the next bit of
the word.
When the entire data word has been sent, the transmitter may
add a Parity Bit that the transmitter generates. The Parity Bit
may be used by the receiver to perform simple error checking.
Then at least one Stop Bit is sent by the transmitter.
When the receiver has received all of the bits in the data
word, it may check for the Parity Bits (both sender and receiver
must agree on whether a Parity Bit is to be used), and then the
receiver looks for a Stop Bit. If the Stop Bit does not appear
when it is supposed to, the UART considers the entire word to be
garbled and will report a Framing Error to the host processor when
the data word is read. The usual cause of a Framing Error is that
the sender and receiver clocks were not running at the same speed,
or that the signal was interrupted.
Regardless of whether the data was received correctly or not,
the UART automatically discards the Start, Parity and Stop bits.
If the sender and receiver are configured identically, these bits
are not passed to the host.
If another word is ready for transmission, the Start Bit for
the new word can be sent as soon as the Stop Bit for the previous
word has been sent.
Because asynchronous data is self synchronizing
,
if there is no data to transmit, the transmission line can be
idle.
Other UART Functions
In addition to the basic job of converting data from parallel
to serial for transmission and from serial to parallel on
reception, a UART will usually provide additional circuits for
signals that can be used to indicate the state of the transmission
media, and to regulate the flow of data in the event that the
remote device is not prepared to accept more data. For example,
when the device connected to the UART is a modem, the modem may
report the presence of a carrier on the phone line while the
computer may be able to instruct the modem to reset itself or to
- not take calls by asserting or disasserting one more more of these
+ not take calls by asserting or disasserting one more of these
extra signals. The function of each of these additional signals is
defined in the EIA RS232-C standard.
The RS232-C and V.24 Standards
In most computer systems, the UART is connected to circuitry
that generates signals that comply with the EIA RS232-C
specification. There is also a CCITT standard named V.24 that
mirrors the specifications included in RS232-C.
RS232-C Bit Assignments (Marks and Spaces)
In RS232-C, a value of 1 is called a
Mark and a value of 0 is
called a Space . When a communication line is
idle, the line is said to be Marking
, or
transmitting continuous 1 values.
The Start bit always has a value of 0 (a
Space). The Stop Bit always has a value of 1
(a Mark). This means that there will always be a Mark (1) to
Space (0) transition on the line at the start of every word,
even when multiple word are transmitted back to back. This
guarantees that sender and receiver can resynchronize their
clocks regardless of the content of the data bits that are being
transmitted.
The idle time between Stop and Start bits does not have to
be an exact multiple (including zero) of the bit rate of the
communication link, but most UARTs are designed this way for
simplicity.
In RS232-C, the "Marking" signal (a 1 ) is
represented by a voltage between -2 VDC and -12 VDC, and a
"Spacing" signal (a 0 ) is represented by a
voltage between 0 and +12 VDC. The transmitter is supposed to
send +12 VDC or -12 VDC, and the receiver is supposed to allow
for some voltage loss in long cables. Some transmitters in low
power devices (like portable computers) sometimes use only +5
VDC and -5 VDC, but these values are still acceptable to a
RS232-C receiver, provided that the cable lengths are
short.
RS232-C Break Signal
RS232-C also specifies a signal called a
Break , which is caused by sending continuous
Spacing values (no Start or Stop bits). When there is no
electricity present on the data circuit, the line is considered
to be sending Break .
The Break signal must be of a duration
longer than the time it takes to send a complete byte plus
Start, Stop and Parity bits. Most UARTs can distinguish between
a Framing Error and a Break, but if the UART cannot do this, the
Framing Error detection can be used to identify Breaks.
In the days of teleprinters, when numerous printers around
the country were wired in series (such as news services), any
unit could cause a Break by temporarily
opening the entire circuit so that no current flowed. This was
used to allow a location with urgent news to interrupt some
other location that was currently sending information.
In modern systems there are two types of Break signals. If
the Break is longer than 1.6 seconds, it is considered a "Modem
Break", and some modems can be programmed to terminate the
conversation and go on-hook or enter the modems' command mode
when the modem detects this signal. If the Break is smaller
than 1.6 seconds, it signifies a Data Break and it is up to the
remote computer to respond to this signal. Sometimes this form
of Break is used as an Attention or Interrupt signal and
sometimes is accepted as a substitute for the ASCII CONTROL-C
character.
Marks and Spaces are also equivalent to Holes
and No Holes
in paper tape systems.
Breaks cannot be generated from paper tape or from any
other byte value, since bytes are always sent with Start and
Stop bit. The UART is usually capable of generating the
continuous Spacing signal in response to a special command
from the host processor.
RS232-C DTE and DCE Devices
The RS232-C specification defines two types of equipment:
the Data Terminal Equipment (DTE) and the Data Carrier Equipment
(DCE). Usually, the DTE device is the terminal (or computer),
and the DCE is a modem. Across the phone line at the other end
of a conversation, the receiving modem is also a DCE device and
the computer that is connected to that modem is a DTE device.
The DCE device receives signals on the pins that the DTE device
transmits on, and vice versa.
When two devices that are both DTE or both DCE must be
connected together without a modem or a similar media translater
between them, a NULL modem must be used. The NULL modem
electrically re-arranges the cabling so that the transmitter
output is connected to the receiver input on the other device,
and vice versa. Similar translations are performed on all of
the control signals so that each device will see what it thinks
are DCE (or DTE) signals from the other device.
The number of signals generated by the DTE and DCE devices
are not symmetrical. The DTE device generates fewer signals for
the DCE device than the DTE device receives from the DCE.
RS232-C Pin Assignments
The EIA RS232-C specification (and the ITU equivalent, V.24)
calls for a twenty-five pin connector (usually a DB25) and
defines the purpose of most of the pins in that
connector.
In the IBM Personal Computer and similar systems, a subset
of RS232-C signals are provided via nine pin connectors (DB9).
The signals that are not included on the PC connector deal
mainly with synchronous operation, and this transmission mode is
not supported by the UART that IBM selected for use in the IBM
PC.
Depending on the computer manufacturer, a DB25, a DB9, or
both types of connector may be used for RS232-C communications.
(The IBM PC also uses a DB25 connector for the parallel printer
interface which causes some confusion.)
Below is a table of the RS232-C signal assignments in the
DB25 and DB9 connectors.
DB25 RS232-C Pin
DB9 IBM PC Pin
EIA Circuit Symbol
CCITT Circuit Symbol
Common Name
Signal Source
Description
1
-
AA
101
PG/FG
-
Frame/Protective Ground
2
3
BA
103
TD
DTE
Transmit Data
3
2
BB
104
RD
DCE
Receive Data
4
7
CA
105
RTS
DTE
Request to Send
5
8
CB
106
CTS
DCE
Clear to Send
6
6
CC
107
DSR
DCE
Data Set Ready
7
5
AV
102
SG/GND
-
Signal Ground
8
1
CF
109
DCD/CD
DCE
Data Carrier Detect
9
-
-
-
-
-
Reserved for Test
10
-
-
-
-
-
Reserved for Test
11
-
-
-
-
-
Reserved for Test
12
-
CI
122
SRLSD
DCE
Sec. Recv. Line Signal Detector
13
-
SCB
121
SCTS
DCE
Secondary Clear to Send
14
-
SBA
118
STD
DTE
Secondary Transmit Data
15
-
DB
114
TSET
DCE
Trans. Sig. Element Timing
16
-
SBB
119
SRD
DCE
Secondary Received Data
17
-
DD
115
RSET
DCE
Receiver Signal Element Timing
18
-
-
141
LOOP
DTE
Local Loopback
19
-
SCA
120
SRS
DTE
Secondary Request to Send
20
4
CD
108.2
DTR
DTE
Data Terminal Ready
21
-
-
-
RDL
DTE
Remote Digital Loopback
22
9
CE
125
RI
DCE
Ring Indicator
23
-
CH
111
DSRS
DTE
Data Signal Rate Selector
24
-
DA
113
TSET
DTE
Trans. Sig. Element Timing
25
-
-
142
-
DCE
Test Mode
Bits, Baud and Symbols
Baud is a measurement of transmission speed in asynchronous
communication. Because of advances in modem communication
technology, this term is frequently misused when describing the
data rates in newer devices.
Traditionally, a Baud Rate represents the number of bits that
are actually being sent over the media, not the amount of data
that is actually moved from one DTE device to the other. The Baud
count includes the overhead bits Start, Stop and Parity that are
generated by the sending UART and removed by the receiving UART.
This means that seven-bit words of data actually take 10 bits to
be completely transmitted. Therefore, a modem capable of moving
300 bits per second from one place to another can normally only
move 30 7-bit words if Parity is used and one Start and Stop bit
are present.
If 8-bit data words are used and Parity bits are also used,
the data rate falls to 27.27 words per second, because it now
takes 11 bits to send the eight-bit words, and the modem still
only sends 300 bits per second.
The formula for converting bytes per second into a baud rate
and vice versa was simple until error-correcting modems came
along. These modems receive the serial stream of bits from the
UART in the host computer (even when internal modems are used the
data is still frequently serialized) and converts the bits back
into bytes. These bytes are then combined into packets and sent
over the phone line using a Synchronous transmission method. This
means that the Stop, Start, and Parity bits added by the UART in
the DTE (the computer) were removed by the modem before
transmission by the sending modem. When these bytes are received
by the remote modem, the remote modem adds Start, Stop and Parity
bits to the words, converts them to a serial format and then sends
them to the receiving UART in the remote computer, who then strips
the Start, Stop and Parity bits.
The reason all these extra conversions are done is so that the
two modems can perform error correction, which means that the
receiving modem is able to ask the sending modem to resend a block
of data that was not received with the correct checksum. This
checking is handled by the modems, and the DTE devices are usually
unaware that the process is occurring.
By striping the Start, Stop and Parity bits, the additional
bits of data that the two modems must share between themselves to
perform error-correction are mostly concealed from the effective
transmission rate seen by the sending and receiving DTE equipment.
For example, if a modem sends ten 7-bit words to another modem
without including the Start, Stop and Parity bits, the sending
modem will be able to add 30 bits of its own information that the
receiving modem can use to do error-correction without impacting
the transmission speed of the real data.
The use of the term Baud is further confused by modems that
perform compression. A single 8-bit word passed over the
telephone line might represent a dozen words that were transmitted
to the sending modem. The receiving modem will expand the data
back to its original content and pass that data to the receiving
DTE.
Modern modems also include buffers that allow the rate that
bits move across the phone line (DCE to DCE) to be a different
speed than the speed that the bits move between the DTE and DCE on
both ends of the conversation. Normally the speed between the DTE
and DCE is higher than the DCE to DCE speed because of the use of
compression by the modems.
Because the number of bits needed to describe a byte varied
during the trip between the two machines plus the differing
bits-per-seconds speeds that are used present on the DTE-DCE and
DCE-DCE links, the usage of the term Baud to describe the overall
communication speed causes problems and can misrepresent the true
transmission speed. So Bits Per Second (bps) is the correct term
to use to describe the transmission rate seen at the DCE to DCE
interface and Baud or Bits Per Second are acceptable terms to use
when a connection is made between two systems with a wired
connection, or if a modem is in use that is not performing
error-correction or compression.
Modern high speed modems (2400, 9600, 14,400, and 19,200bps)
in reality still operate at or below 2400 baud, or more
accurately, 2400 Symbols per second. High speed modem are able to
encode more bits of data into each Symbol using a technique called
Constellation Stuffing, which is why the effective bits per second
rate of the modem is higher, but the modem continues to operate
within the limited audio bandwidth that the telephone system
provides. Modems operating at 28,800 and higher speeds have
variable Symbol rates, but the technique is the same.
The IBM Personal Computer UART
Starting with the original IBM Personal Computer, IBM selected
the National Semiconductor INS8250 UART for use in the IBM PC
Parallel/Serial Adapter. Subsequent generations of compatible
computers from IBM and other vendors continued to use the INS8250
or improved versions of the National Semiconductor UART
family.
National Semiconductor UART Family Tree
There have been several versions and subsequent generations
of the INS8250 UART. Each major version is described
below.
INS8250 -> INS8250B
\
\
\-> INS8250A -> INS82C50A
\
\
\-> NS16450 -> NS16C450
\
\
\-> NS16550 -> NS16550A -> PC16550D
INS8250
This part was used in the original IBM PC and IBM
PC/XT. The original name for this part was the INS8250
ACE (Asynchronous Communications Element) and it is made
from NMOS technology.
The 8250 uses eight I/O ports and has a one-byte send
and a one-byte receive buffer. This original UART has
several race conditions and other flaws. The original IBM
BIOS includes code to work around these flaws, but this
made the BIOS dependent on the flaws being present, so
subsequent parts like the 8250A, 16450 or 16550 could not
be used in the original IBM PC or IBM PC/XT.
INS8250-B
This is the slower speed of the INS8250 made from NMOS
technology. It contains the same problems as the original
INS8250.
INS8250A
An improved version of the INS8250 using XMOS
technology with various functional flaws corrected. The
INS8250A was used initially in PC clone computers by
vendors who used clean
BIOS designs. Because
of the corrections in the chip, this part could not be
used with a BIOS compatible with the INS8250 or
INS8250B.
INS82C50A
This is a CMOS version (low power consumption) of the
INS8250A and has similar functional
characteristics.
NS16450
Same as NS8250A with improvements so it can be used
with faster CPU bus designs. IBM used this part in the
IBM AT and updated the IBM BIOS to no longer rely on the
bugs in the INS8250.
NS16C450
This is a CMOS version (low power consumption) of the
NS16450.
NS16550
Same as NS16450 with a 16-byte send and receive buffer
but the buffer design was flawed and could not be reliably
be used.
NS16550A
Same as NS16550 with the buffer flaws corrected. The
16550A and its successors have become the most popular
UART design in the PC industry, mainly due it its ability
to reliably handle higher data rates on operating systems
with sluggish interrupt response times.
NS16C552
This component consists of two NS16C550A CMOS UARTs in
a single package.
PC16550D
Same as NS16550A with subtle flaws corrected. This is
revision D of the 16550 family and is the latest design
available from National Semiconductor.
The NS16550AF and the PC16550D are the same thing
National reorganized their part numbering system a few years
ago, and the NS16550AFN no longer exists by that name. (If you
have a NS16550AFN, look at the date code on the part, which is a
four digit number that usually starts with a nine. The first
two digits of the number are the year, and the last two digits
are the week in that year when the part was packaged. If you
have a NS16550AFN, it is probably a few years old.)
The new numbers are like PC16550DV, with minor differences
in the suffix letters depending on the package material and its
shape. (A description of the numbering system can be found
below.)
It is important to understand that in some stores, you may
pay $15(US) for a NS16550AFN made in 1990 and in the next
bin are the new PC16550DN parts with minor fixes that National
has made since the AFN part was in production, the PC16550DN was
probably made in the past six months and it costs half (as low
as $5(US) in volume) as much as the NS16550AFN because they
are readily available.
As the supply of NS16550AFN chips continues to shrink, the
price will probably continue to increase until more people
discover and accept that the PC16550DN really has the same
function as the old part number.
National Semiconductor Part Numbering System
The older NSnnnnnrqp part
numbers are now of the format
PCnnnnnrgp .
The r is the revision field. The
current revision of the 16550 from National Semiconductor is
D .
The p is the package-type field.
The types are:
"F"
QFP
(quad flat pack) L lead type
"N"
DIP
(dual inline package) through hole straight lead
type
"V"
LPCC
(lead plastic chip carrier) J lead type
The g is the product grade field.
If an I precedes the package-type letter, it
indicates an industrial
grade part, which has
higher specs than a standard part but not as high as Military
Specification (Milspec) component. This is an optional
field.
So what we used to call a NS16550AFN (DIP Package) is now
called a PC16550DN or PC16550DIN.
Other Vendors and Similar UARTs
Over the years, the 8250, 8250A, 16450 and 16550 have been
licensed or copied by other chip vendors. In the case of the
8250, 8250A and 16450, the exact circuit (the
megacell
) was licensed to many vendors, including
Western Digital and Intel. Other vendors reverse-engineered the
part or produced emulations that had similar behavior.
In internal modems, the modem designer will frequently emulate
the 8250A/16450 with the modem microprocessor, and the emulated
UART will frequently have a hidden buffer consisting of several
hundred bytes. Because of the size of the buffer, these
emulations can be as reliable as a 16550A in their ability to
handle high speed data. However, most operating systems will
still report that the UART is only a 8250A or 16450, and may not
make effective use of the extra buffering present in the emulated
UART unless special drivers are used.
Some modem makers are driven by market forces to abandon a
design that has hundreds of bytes of buffer and instead use a
16550A UART so that the product will compare favorably in market
comparisons even though the effective performance may be lowered
by this action.
A common misconception is that all parts with
16550A
written on them are identical in performance.
There are differences, and in some cases, outright flaws in most
of these 16550A clones.
When the NS16550 was developed, the National Semiconductor
obtained several patents on the design and they also limited
licensing, making it harder for other vendors to provide a chip
with similar features. Because of the patents, reverse-engineered
designs and emulations had to avoid infringing the claims covered
by the patents. Subsequently, these copies almost never perform
exactly the same as the NS16550A or PC16550D, which are the parts
most computer and modem makers want to buy but are sometimes
unwilling to pay the price required to get the genuine
part.
Some of the differences in the clone 16550A parts are
unimportant, while others can prevent the device from being used
at all with a given operating system or driver. These differences
may show up when using other drivers, or when particular
combinations of events occur that were not well tested or
considered in the Windows driver. This is because most modem
vendors and 16550-clone makers use the Microsoft drivers from
Windows for Workgroups 3.11 and the Microsoft MS-DOS utility as the
primary tests for compatibility with the NS16550A. This
over-simplistic criteria means that if a different operating
system is used, problems could appear due to subtle differences
between the clones and genuine components.
National Semiconductor has made available a program named
COMTEST that performs compatibility
tests independent of any OS drivers. It should be remembered that
the purpose of this type of program is to demonstrate the flaws in
the products of the competition, so the program will report major
as well as extremely subtle differences in behavior in the part
being tested.
In a series of tests performed by the author of this document
in 1994, components made by National Semiconductor, TI, StarTech,
and CMD as well as megacells and emulations embedded in internal
modems were tested with COMTEST. A difference count for some of
these components is listed below. Because these tests were
performed in 1994, they may not reflect the current performance of
the given product from a vendor.
It should be noted that COMTEST normally aborts when an
excessive number or certain types of problems have been detected.
As part of this testing, COMTEST was modified so that it would not
abort no matter how many differences were encountered.
Vendor
Part Number
Errors (aka "differences" reported)
National
(PC16550DV)
0
National
(NS16550AFN)
0
National
(NS16C552V)
0
TI
(TL16550AFN)
3
CMD
(16C550PE)
19
StarTech
(ST16C550J)
23
Rockwell
Reference modem with internal 16550 or an
emulation (RC144DPi/C3000-25)
117
Sierra
Modem with an internal 16550
(SC11951/SC11351)
91
To date, the author of this document has not found any
non-National parts that report zero differences using the
COMTEST program. It should also be noted that National has had
five versions of the 16550 over the years and the newest parts
behave a bit differently than the classic NS16550AFN that is
considered the benchmark for functionality. COMTEST appears to
turn a blind eye to the differences within the National product
line and reports no errors on the National parts (except for the
original 16550) even when there are official erratas that
describe bugs in the A, B and C revisions of the parts, so this
bias in COMTEST must be taken into account.
It is important to understand that a simple count of
differences from COMTEST does not reveal a lot about what
differences are important and which are not. For example, about
half of the differences reported in the two modems listed above
that have internal UARTs were caused by the clone UARTs not
supporting five- and six-bit character modes. The real 16550,
16450, and 8250 UARTs all support these modes and COMTEST checks
the functionality of these modes so over fifty differences are
reported. However, almost no modern modem supports five- or
six-bit characters, particularly those with error-correction and
compression capabilities. This means that the differences related
to five- and six-bit character modes can be discounted.
Many of the differences COMTEST reports have to do with
timing. In many of the clone designs, when the host reads from
one port, the status bits in some other port may not update in the
same amount of time (some faster, some slower) as a
real NS16550AFN and COMTEST looks for these
differences. This means that the number of differences can be
misleading in that one device may only have one or two differences
but they are extremely serious, and some other device that updates
the status registers faster or slower than the reference part
(that would probably never affect the operation of a properly
written driver) could have dozens of differences reported.
COMTEST can be used as a screening tool to alert the
administrator to the presence of potentially incompatible
components that might cause problems or have to be handled as a
special case.
If you run COMTEST on a 16550 that is in a modem or a modem is
attached to the serial port, you need to first issue a ATE0&W
command to the modem so that the modem will not echo any of the
test characters. If you forget to do this, COMTEST will report at
least this one difference:
Error (6)...Timeout interrupt failed: IIR = c1 LSR = 61
8250/16450/16550 Registers
The 8250/16450/16550 UART occupies eight contiguous I/O port
addresses. In the IBM PC, there are two defined locations for
these eight ports and they are known collectively as COM1 and
COM2. The makers of PC-clones and add-on cards have created two
additional areas known as COM3 and COM4, but these extra COM ports
conflict with other hardware on some systems. The most common
conflict is with video adapters that provide IBM 8514
emulation.
COM1 is located from 0x3f8 to 0x3ff and normally uses IRQ 4
COM2 is located from 0x2f8 to 0x2ff and normally uses IRQ 3 COM3
is located from 0x3e8 to 0x3ef and has no standardized IRQ COM4 is
located from 0x2e8 to 0x2ef and has no standardized IRQ.
A description of the I/O ports of the 8250/16450/16550 UART is
provided below.
I/O Port
Access Allowed
Description
+0x00
write (DLAB==0)
Transmit Holding Register
(THR). Information written to this port are
treated as data words and will be transmitted by the
UART.
+0x00
read (DLAB==0)
Receive Buffer Register (RBR). Any
data words received by the UART form the serial link are
accessed by the host by reading this
port.
+0x00
write/read (DLAB==1)
Divisor Latch LSB (DLL) This value
will be divided from the master input clock (in the IBM
PC, the master clock is 1.8432MHz) and the resulting
clock will determine the baud rate of the UART. This
register holds bits 0 thru 7 of the
divisor.
+0x01
write/read (DLAB==1)
Divisor Latch MSB (DLH) This value
will be divided from the master input clock (in the IBM
PC, the master clock is 1.8432MHz) and the resulting
clock will determine the baud rate of the UART. This
register holds bits 8 thru 15 of the
divisor.
+0x01
write/read (DLAB==0)
Interrupt Enable Register
(IER) The 8250/16450/16550 UART
classifies events into one of four categories.
Each category can be configured to generate an
interrupt when any of the events occurs. The
8250/16450/16550 UART generates a single external
interrupt signal regardless of how many events in
the enabled categories have occurred. It is up to
the host processor to respond to the interrupt and
then poll the enabled interrupt categories
(usually all categories have interrupts enabled)
to determine the true cause(s) of the
interrupt.
Bit 7
Reserved, always 0.
Bit 6
Reserved, always 0.
Bit 5
Reserved, always 0.
Bit 4
Reserved, always 0.
Bit 3
Enable Modem Status Interrupt (EDSSI). Setting
this bit to "1" allows the UART to generate an
interrupt when a change occurs on one or more of the
status lines.
Bit 2
Enable Receiver Line Status Interrupt (ELSI)
Setting this bit to "1" causes the UART to generate
an interrupt when the an error (or a BREAK signal)
has been detected in the incoming data.
Bit 1
Enable Transmitter Holding Register Empty
Interrupt (ETBEI) Setting this bit to "1" causes the
UART to generate an interrupt when the UART has room
for one or more additional characters that are to be
transmitted.
Bit 0
Enable Received Data Available Interrupt
(ERBFI) Setting this bit to "1" causes the UART to
generate an interrupt when the UART has received
enough characters to exceed the trigger level of the
FIFO, or the FIFO timer has expired (stale data), or
a single character has been received when the FIFO
is disabled.
+0x02
write
FIFO Control Register (FCR)
(This port does not exist on the 8250 and 16450
UART.)
Bit 7
Receiver Trigger Bit #1
Bit 6
Receiver Trigger Bit
#0 These two bits control at what
point the receiver is to generate an interrupt
when the FIFO is active.
7
6
How many words are received
before an interrupt is generated
0
0
1
0
1
4
1
0
8
1
1
14
Bit 5
Reserved, always 0.
Bit 4
Reserved, always 0.
Bit 3
DMA Mode Select. If Bit 0 is
set to "1" (FIFOs enabled), setting this bit changes
the operation of the -RXRDY and -TXRDY signals from
Mode 0 to Mode 1.
Bit 2
Transmit FIFO Reset. When a
"1" is written to this bit, the contents of the FIFO
are discarded. Any word currently being transmitted
will be sent intact. This function is useful in
aborting transfers.
Bit 1
Receiver FIFO Reset. When a
"1" is written to this bit, the contents of the FIFO
are discarded. Any word currently being assembled
in the shift register will be received
intact.
Bit 0
16550 FIFO Enable. When set,
both the transmit and receive FIFOs are enabled.
Any contents in the holding register, shift
registers or FIFOs are lost when FIFOs are enabled
or disabled.
+0x02
read
Interrupt Identification
Register
Bit 7
FIFOs enabled. On the
8250/16450 UART, this bit is zero.
Bit 6
FIFOs enabled. On the
8250/16450 UART, this bit is zero.
Bit 5
Reserved, always 0.
Bit 4
Reserved, always 0.
Bit 3
Interrupt ID Bit #2. On the
8250/16450 UART, this bit is zero.
Bit 2
Interrupt ID Bit #1
Bit 1
Interrupt ID Bit #0.These three
bits combine to report the category of event that
caused the interrupt that is in progress. These
categories have priorities, so if multiple
categories of events occur at the same time, the
UART will report the more important events first and
the host must resolve the events in the order they
are reported. All events that caused the current
interrupt must be resolved before any new interrupts
will be generated. (This is a limitation of the PC
architecture.)
2
1
0
Priority
Description
0
1
1
First
Received Error (OE, PE, BI, or
FE)
0
1
0
Second
Received Data Available
1
1
0
Second
Trigger level identification
(Stale data in receive buffer)
0
0
1
Third
Transmitter has room for more
words (THRE)
0
0
0
Fourth
Modem Status Change (-CTS, -DSR,
-RI, or -DCD)
Bit 0
Interrupt Pending Bit. If this
bit is set to "0", then at least one interrupt is
pending.
+0x03
write/read
Line Control Register
(LCR)
Bit 7
Divisor Latch Access Bit
(DLAB). When set, access to the data
transmit/receive register (THR/RBR) and the
Interrupt Enable Register (IER) is disabled. Any
access to these ports is now redirected to the
Divisor Latch Registers. Setting this bit, loading
the Divisor Registers, and clearing DLAB should be
done with interrupts disabled.
Bit 6
Set Break. When set to "1",
the transmitter begins to transmit continuous
Spacing until this bit is set to "0". This
overrides any bits of characters that are being
transmitted.
Bit 5
Stick Parity. When parity is
enabled, setting this bit causes parity to always be
"1" or "0", based on the value of Bit 4.
Bit 4
Even Parity Select (EPS). When
parity is enabled and Bit 5 is "0", setting this bit
causes even parity to be transmitted and expected.
Otherwise, odd parity is used.
Bit 3
Parity Enable (PEN). When set
to "1", a parity bit is inserted between the last
bit of the data and the Stop Bit. The UART will
also expect parity to be present in the received
data.
Bit 2
Number of Stop Bits (STB). If
set to "1" and using 5-bit data words, 1.5 Stop Bits
are transmitted and expected in each data word. For
6, 7 and 8-bit data words, 2 Stop Bits are
transmitted and expected. When this bit is set to
"0", one Stop Bit is used on each data word.
Bit 1
Word Length Select Bit #1
(WLSB1)
Bit 0
Word Length Select Bit #0
(WLSB0)
Together these
bits specify the number of bits in each data
word.
1
0
Word
Length
0
0
5 Data
Bits
0
1
6 Data
Bits
1
0
7 Data
Bits
1
1
8 Data
Bits
+0x04
write/read
Modem Control Register
(MCR)
Bit 7
Reserved, always 0.
Bit 6
Reserved, always 0.
Bit 5
Reserved, always 0.
Bit 4
Loop-Back Enable. When set to "1", the UART
transmitter and receiver are internally connected
together to allow diagnostic operations. In
addition, the UART modem control outputs are
connected to the UART modem control inputs. CTS is
connected to RTS, DTR is connected to DSR, OUT1 is
connected to RI, and OUT 2 is connected to
DCD.
Bit 3
OUT 2. An auxiliary output that the host
processor may set high or low. In the IBM PC serial
adapter (and most clones), OUT 2 is used to
tri-state (disable) the interrupt signal from the
8250/16450/16550 UART.
Bit 2
OUT 1. An auxiliary output that the host
processor may set high or low. This output is not
used on the IBM PC serial adapter.
Bit 1
Request to Send (RTS). When set to "1", the
output of the UART -RTS line is Low
(Active).
Bit 0
Data Terminal Ready (DTR). When set to "1",
the output of the UART -DTR line is Low
(Active).
+0x05
write/read
Line Status Register
(LSR)
Bit 7
Error in Receiver FIFO. On the 8250/16450
UART, this bit is zero. This bit is set to "1" when
any of the bytes in the FIFO have one or more of the
following error conditions: PE, FE, or BI.
Bit 6
Transmitter Empty (TEMT). When set to "1",
there are no words remaining in the transmit FIFO
or the transmit shift register. The transmitter is
completely idle.
Bit 5
Transmitter Holding Register Empty (THRE).
When set to "1", the FIFO (or holding register) now
has room for at least one additional word to
transmit. The transmitter may still be transmitting
when this bit is set to "1".
Bit 4
Break Interrupt (BI). The receiver has
detected a Break signal.
Bit 3
Framing Error (FE). A Start Bit was detected
but the Stop Bit did not appear at the expected
time. The received word is probably
garbled.
Bit 2
Parity Error (PE). The parity bit was
incorrect for the word received.
Bit 1
Overrun Error (OE). A new word was received
and there was no room in the receive buffer. The
newly-arrived word in the shift register is
discarded. On 8250/16450 UARTs, the word in the
holding register is discarded and the newly- arrived
word is put in the holding register.
Bit 0
Data Ready (DR) One or more words are in the
receive FIFO that the host may read. A word must be
completely received and moved from the shift
register into the FIFO (or holding register for
8250/16450 designs) before this bit is set.
+0x06
write/read
Modem Status Register
(MSR)
Bit 7
Data Carrier Detect (DCD). Reflects the state
of the DCD line on the UART.
Bit 6
Ring Indicator (RI). Reflects the state of the
RI line on the UART.
Bit 5
Data Set Ready (DSR). Reflects the state of
the DSR line on the UART.
Bit 4
Clear To Send (CTS). Reflects the state of the
CTS line on the UART.
Bit 3
Delta Data Carrier Detect (DDCD). Set to "1"
- if the -DCD line has changed state one more more
+ if the -DCD line has changed state one more
times since the last time the MSR was read by the
host.
Bit 2
Trailing Edge Ring Indicator (TERI). Set to
"1" if the -RI line has had a low to high transition
since the last time the MSR was read by the
host.
Bit 1
Delta Data Set Ready (DDSR). Set to "1" if the
- -DSR line has changed state one more more times
+ -DSR line has changed state one more times
since the last time the MSR was read by the
host.
Bit 0
Delta Clear To Send (DCTS). Set to "1" if the
- -CTS line has changed state one more more times
+ -CTS line has changed state one more times
since the last time the MSR was read by the
host.
+0x07
write/read
Scratch Register (SCR). This register performs no
function in the UART. Any value can be written by the
host to this location and read by the host later
on.
Beyond the 16550A UART
Although National Semiconductor has not offered any components
compatible with the 16550 that provide additional features,
various other vendors have. Some of these components are
described below. It should be understood that to effectively
utilize these improvements, drivers may have to be provided by the
chip vendor since most of the popular operating systems do not
support features beyond those provided by the 16550.
ST16650
By default this part is similar to the NS16550A, but an
extended 32-byte send and receive buffer can be optionally
enabled. Made by StarTech.
TIL16660
By default this part behaves similar to the NS16550A,
but an extended 64-byte send and receive buffer can be
optionally enabled. Made by Texas Instruments.
Hayes ESP
This proprietary plug-in card contains a 2048-byte send
and receive buffer, and supports data rates to
230.4Kbit/sec. Made by Hayes.
In addition to these dumb
UARTs, many vendors
produce intelligent serial communication boards. This type of
design usually provides a microprocessor that interfaces with
several UARTs, processes and buffers the data, and then alerts the
main PC processor when necessary. Because the UARTs are not
directly accessed by the PC processor in this type of
communication system, it is not necessary for the vendor to use
UARTs that are compatible with the 8250, 16450, or the 16550 UART.
This leaves the designer free to components that may have better
performance characteristics.
Configuring the sio driver
The sio driver provides support for
NS8250-, NS16450-, NS16550 and NS16550A-based EIA RS-232C (CCITT
V.24) communications interfaces. Several multiport cards are
supported as well. See the &man.sio.4;
manual page for detailed technical documentation.
Digi International (DigiBoard) PC/8
Contributed by &a.awebster;. 26 August
1995.
Here is a config snippet from a machine with a Digi
International PC/8 with 16550. It has 8 modems connected to these
8 lines, and they work just great. Do not forget to add
options COM_MULTIPORT or it will not work very
well!
device sio4 at isa? port 0x100 tty flags 0xb05
device sio5 at isa? port 0x108 tty flags 0xb05
device sio6 at isa? port 0x110 tty flags 0xb05
device sio7 at isa? port 0x118 tty flags 0xb05
device sio8 at isa? port 0x120 tty flags 0xb05
device sio9 at isa? port 0x128 tty flags 0xb05
device sio10 at isa? port 0x130 tty flags 0xb05
device sio11 at isa? port 0x138 tty flags 0xb05 irq 9 vector siointr
The trick in setting this up is that the MSB of the flags
represent the last SIO port, in this case 11 so flags are
0xb05.
Boca 16
Contributed by &a.whiteside;. 26 August
1995.
The procedures to make a Boca 16 port board with FreeBSD are
pretty straightforward, but you will need a couple things to make
it work:
You either need the kernel sources installed so you can
recompile the necessary options or you will need someone else
to compile it for you. The 2.0.5 default kernel does
not come with multiport support enabled
and you will need to add a device entry for each port
anyways.
Two, you will need to know the interrupt and IO setting
for your Boca Board so you can set these options properly in
the kernel.
One important note — the actual UART chips for the Boca
16 are in the connector box, not on the internal board itself. So
if you have it unplugged, probes of those ports will fail. I have
never tested booting with the box unplugged and plugging it back
in, and I suggest you do not either.
If you do not already have a custom kernel configuration file
set up, refer to Kernel
Configuration for general procedures. The following are
the specifics for the Boca 16 board and assume you are using the
kernel name MYKERNEL and editing with vi.
Add the line
options COM_MULTIPORT
to the config file.
Where the current device
sion lines are, you
will need to add 16 more devices. Only the last device
includes the interrupt vector for the board. (See the
&man.sio.4; manual page for detail as
to why.) The following example is for a Boca Board with an
interrupt of 3, and a base IO address 100h. The IO address
for Each port is +8 hexadecimal from the previous port, thus
the 100h, 108h, 110h... addresses.
device sio1 at isa? port 0x100 tty flags 0x1005
device sio2 at isa? port 0x108 tty flags 0x1005
device sio3 at isa? port 0x110 tty flags 0x1005
device sio4 at isa? port 0x118 tty flags 0x1005
…
device sio15 at isa? port 0x170 tty flags 0x1005
device sio16 at isa? port 0x178 tty flags 0x1005 irq 3 vector siointr
The flags entry must be changed from
this example unless you are using the exact same sio
assignments. Flags are set according to
0xM YY
where M indicates the minor number
of the master port (the last port on a Boca 16) and
YY indicates if FIFO is enabled or
disabled(enabled), IRQ sharing is used(yes) and if there is an
AST/4 compatible IRQ control register(no). In this example,
flags 0x1005 indicates that
the master port is sio16. If I added another board and
assigned sio17 through sio28, the flags for all 16 ports on
that board would be 0x1C05, where 1C
indicates the minor number of the master port. Do not change
the 05 setting.
Save and complete the kernel configuration, recompile,
install and reboot. Presuming you have successfully installed
the recompiled kernel and have it set to the correct address
and IRQ, your boot message should indicate the successful
probe of the Boca ports as follows: (obviously the sio
numbers, IO and IRQ could be different)
sio1 at 0x100-0x107 flags 0x1005 on isa
sio1: type 16550A (multiport)
sio2 at 0x108-0x10f flags 0x1005 on isa
sio2: type 16550A (multiport)
sio3 at 0x110-0x117 flags 0x1005 on isa
sio3: type 16550A (multiport)
sio4 at 0x118-0x11f flags 0x1005 on isa
sio4: type 16550A (multiport)
sio5 at 0x120-0x127 flags 0x1005 on isa
sio5: type 16550A (multiport)
sio6 at 0x128-0x12f flags 0x1005 on isa
sio6: type 16550A (multiport)
sio7 at 0x130-0x137 flags 0x1005 on isa
sio7: type 16550A (multiport)
sio8 at 0x138-0x13f flags 0x1005 on isa
sio8: type 16550A (multiport)
sio9 at 0x140-0x147 flags 0x1005 on isa
sio9: type 16550A (multiport)
sio10 at 0x148-0x14f flags 0x1005 on isa
sio10: type 16550A (multiport)
sio11 at 0x150-0x157 flags 0x1005 on isa
sio11: type 16550A (multiport)
sio12 at 0x158-0x15f flags 0x1005 on isa
sio12: type 16550A (multiport)
sio13 at 0x160-0x167 flags 0x1005 on isa
sio13: type 16550A (multiport)
sio14 at 0x168-0x16f flags 0x1005 on isa
sio14: type 16550A (multiport)
sio15 at 0x170-0x177 flags 0x1005 on isa
sio15: type 16550A (multiport)
sio16 at 0x178-0x17f irq 3 flags 0x1005 on isa
sio16: type 16550A (multiport master)
If the messages go by too fast to see,
&prompt.root; dmesg | more
will show you the boot messages.
Next, appropriate entries in /dev for
the devices must be made using the
/dev/MAKEDEV script. After becoming
root:
&prompt.root; cd /dev
&prompt.root; ./MAKEDEV tty1
&prompt.root; ./MAKEDEV cua1
(everything in between)
&prompt.root; ./MAKEDEV ttyg
&prompt.root; ./MAKEDEV cuag
If you do not want or need call-out devices for some
reason, you can dispense with making the
cua* devices.
If you want a quick and sloppy way to make sure the
devices are working, you can simply plug a modem into each
port and (as root)
&prompt.root; echo at > ttyd*
for each device you have made. You
should see the RX lights flash for each
working port.
Support for Cheap Multi-UART Cards
Contributed by Helge Oldach
hmo@sep.hamburg.com , September
1999
Ever wondered about FreeBSD support for your 20$ multi-I/O
card with two (or more) COM ports, sharing IRQs? Here's
how:
Usually the only option to support these kind of boards is to
use a distinct IRQ for each port. For example, if your CPU board
has an on-board COM1 port (aka
sio0 –I/O address 0x3F8 and IRQ 4)
and you have an extension board with two UARTs, you will commonly
need to configure them as COM2 (aka
sio1 –I/O address 0x2F8 and IRQ 3),
and the third port (aka sio2 ) as I/O
0x3E8 and IRQ 5. Obviously this is a waste of IRQ resources, as
it should be basically possible to run both extension board ports
using a single IRQ with the COM_MULTIPORT
configuration described in the previous sections.
Such cheap I/O boards commonly have a 4 by 3 jumper matrix for
the COM ports, similar to the following:
o o o *
Port A |
o * o *
Port B |
o * o o
IRQ 2 3 4 5
Shown here is port A wired for IRQ 5 and port B wired for IRQ
3. The IRQ columns on your specific board may vary—other
boards may supply jumpers for IRQs 3, 4, 5, and 7 instead.
One could conclude that wiring both ports for IRQ 3 using a
handcrafted wire-made jumper covering all three connection points
in the IRQ 3 column would solve the issue, but no. You cannot
duplicate IRQ 3 because the output drivers of each UART are wired
in a totem pole
fashion, so if one of the UARTs
drives IRQ 3, the output signal will not be what you would expect.
Depending on the implementation of the extension board or your
motherboard, the IRQ 3 line will continuously stay up, or always
stay low.
You need to decouple the IRQ drivers for the two UARTs, so
that the IRQ line of the board only goes up if (and only if) one
of the UARTs asserts a IRQ, and stays low otherwise. The solution
was proposed by Joerg Wunsch
j@ida.interface-business.de : To solder up a
wired-or consisting of two diodes (Germanium or Schottky-types
strongly preferred) and a 1 kOhm resistor. Here is the schematic,
starting from the 4 by 3 jumper field above:
Diode
+---------->|-------+
/ |
o * o o | 1 kOhm
Port A +----|######|-------+
o * o o | |
Port B `-------------------+ ==+==
o * o o | Ground
\ |
+--------->|-------+
IRQ 2 3 4 5 Diode
The cathodes of the diodes are connected to a common point,
together with a 1 kOhm pull-down resistor. It is essential to
connect the resistor to ground to avoid floating of the IRQ line
on the bus.
Now we are ready to configure a kernel. Staying with this
example, we would configure:
# standard on-board COM1 port
device sio0 at isa? port "IO_COM1" tty flags 0x10
# patched-up multi-I/O extension board
options COM_MULTIPORT
device sio1 at isa? port "IO_COM2" tty flags 0x205
device sio2 at isa? port "IO_COM3" tty flags 0x205 irq 3
Note that the flags setting for
sio1 and sio2 is
truly essential; refer to
&man.sio.4; for details. (Generally, the 2 in
the "flags" attribute refers to sio 2
which holds the IRQ, and you surely want a 5
low nibble.) With kernel verbose mode turned on this should yield
something similar to this:
sio0: irq maps: 0x1 0x11 0x1 0x1
sio0 at 0x3f8-0x3ff irq 4 flags 0x10 on isa
sio0: type 16550A
sio1: irq maps: 0x1 0x9 0x1 0x1
sio1 at 0x2f8-0x2ff flags 0x205 on isa
sio1: type 16550A (multiport)
sio2: irq maps: 0x1 0x9 0x1 0x1
sio2 at 0x3e8-0x3ef irq 3 flags 0x205 on isa
sio2: type 16550A (multiport master)
Though /sys/i386/isa/sio.c is somewhat
cryptic with its use of the irq maps
array above,
the basic idea is that you observe 0x1 in the
first, third, and fourth place. This means that the corresponding
IRQ was set upon output and cleared after, which is just what we
would expect. If your kernel does not display this behavior, most
likely there is something wrong with your wiring.
Configuring the cy driver
Contributed by Alex Nash. 6 June
1996.
The Cyclades multiport cards are based on the
cy driver instead of the usual
sio driver used by other multiport cards.
Configuration is a simple matter of:
Add the cy device to your kernel configuration
(note that your irq and iomem settings may differ).
device cy0 at isa? tty irq 10 iomem 0xd4000 iosiz 0x2000 vector cyintr
Rebuild and
install the new kernel.
Make the device
nodes by typing (the following example assumes an
8-port board):
&prompt.root; cd /dev
&prompt.root; for i in 0 1 2 3 4 5 6 7;do ./MAKEDEV cuac$i ttyc$i;done
If appropriate, add dialup
entries to /etc/ttys by
duplicating serial device (ttyd ) entries and
using ttyc in place of
ttyd . For example:
ttyc0 "/usr/libexec/getty std.38400" unknown on insecure
ttyc1 "/usr/libexec/getty std.38400" unknown on insecure
ttyc2 "/usr/libexec/getty std.38400" unknown on insecure
…
ttyc7 "/usr/libexec/getty std.38400" unknown on insecure
Reboot with the new kernel.
Configuring the si driver
Contributed by &a.nsayer;. 25 March
1998.
The Specialix SI/XIO and SX multiport cards use the
si driver. A single machine can
have up to 4 host cards. The following host cards
are supported:
ISA SI/XIO host card (2 versions)
EISA SI/XIO host card
PCI SI/XIO host card
ISA SX host card
PCI SX host card
Although the SX and SI/XIO host cards look markedly different,
their functionality are basically the same. The host cards do not
use I/O locations, but instead require a 32K chunk of memory. The
factory configuration for ISA cards places this at
0xd0000-0xd7fff .
They also require an IRQ. PCI cards will, of course, auto-configure
themselves.
You can attach up to 4 external modules to each host card. The
external modules contain either 4 or 8 serial ports. They come in
the following varieties:
SI 4 or 8 port modules. Up to 57600 bps on each port
supported.
XIO 8 port modules. Up to 115200 bps on each port
supported. One type of XIO module has 7 serial and 1 parallel
port.
SXDC 8 port modules. Up to 921600 bps on each port
supported. Like XIO, a module is available with one parallel
port as well.
To configure an ISA host card, add the following line to your
kernel configuration
file, changing the numbers as appropriate:
device si0 at isa? tty iomem 0xd0000 irq 11
Valid IRQ numbers are 9, 10, 11, 12 and 15 for SX ISA host cards
and 11, 12 and 15 for SI/XIO ISA host cards.
To configure an EISA or PCI host card, use this line:
device si0
After adding the configuration entry, rebuild and install your
new kernel.
After rebooting with the new kernel, you need to make the device nodes in /dev. The
MAKEDEV script will take care of this for you.
Count how many total ports you have and type:
&prompt.root; cd /dev
&prompt.root; ./MAKEDEV ttyAnn cuaAnn
(where nn is the number of
ports)
If you want login prompts to appear on these ports, you will
need to add lines like this to /etc/ttys :
ttyA01 "/usr/libexec/getty std.9600" vt100 on insecure
Change the terminal type as appropriate. For modems,
dialup or unknown is
fine.
* Parallel ports
* Modems
* Network cards
* Keyboards
Mice
Contributed by Joel Sutton
jsutton@bbcon.com.au January 2000
FreeBSD supports a variety of different mice via the PS/2, serial
and USB ports. Most users choose to use the mouse daemon to handle
their mouse because it allows interaction in both X and on the system
console. For more information on the mouse daemon refer to
&man.moused.8;. The examples throughout this section assume that
the mouse daemon is being used.
This section contains the names of specific products that the
author has confirmed will work with FreeBSD. Other similar devices
not listed may also be supported.
PS/2
System Configuration
To ensure that your PS/2 mouse functions correctly with the
mouse daemon you will need to include the following text in
/etc/rc.conf
moused_enable="YES"
moused_type="ps/2"
moused_port="/dev/psm0"
Known Compatible Devices
Logitech First Mouse - Three Button
Microsoft Serial - PS/2 Compatible Mouse
Serial
System Configuration
To ensure that your serial mouse functions correctly with the
mouse daemon you will need to include the following text in
/etc/rc.conf . This example assumes that the
mouse is connected to COM1: and can be
automatically recognized by the mouse daemon.
moused_enable="YES"
moused_type="auto"
moused_port="/dev/cuaa0"
See the &man.moused.8; manual page for a detailed description
of how to configure the mouse daemon to work with specific types
of serial mice.
Known Compatible Devices
Generic Microsoft Compatible Mice
Logitech First Mouse - Three Button
Microsoft Serial - PS/2 Compatible Mouse
USB
System Configuration
The USB device drivers are a relatively new addition to
FreeBSD and have not yet been included in the GENERIC kernel. The
following procedure is an example of how to setup the relevant
drivers on a typical system.
Add the ums device to the usb
section of your kernel
configuration. For example:
controller usb0 controller uhci0 device ums0
Rebuild and
install the new kernel.
Make the device
node by typing:
&prompt.root; cd /dev
&prompt.root; sh MAKEDEV ums0
Include the following text in
/etc/rc.conf to ensure correct operation
of the mouse daemon:
moused_enable="YES"
moused_type="auto"
moused_port="/dev/ums0"
Reboot the system.
&prompt.root; shutdown -r now
Known Compatible Devices
Logitech TrackMan - Marble Wheel
* Other
]]>
Storage Devices
Using ESDI hard disks
Copyright © 1995, &a.wilko;. 24
September 1995.
ESDI is an acronym that means Enhanced Small Device Interface. It
is loosely based on the good old ST506/412 interface originally
devised by Seagate Technology, the makers of the first affordable
5.25" winchester disk.
The acronym says Enhanced, and rightly so. In the first place the
speed of the interface is higher, 10 or 15 Mbits/second instead of the
5 Mbits/second of ST412 interfaced drives. Secondly some higher level
commands are added, making the ESDI interface somewhat 'smarter' to
the operating system driver writers. It is by no means as smart as
SCSI by the way. ESDI is standardized by ANSI.
Capacities of the drives are boosted by putting more sectors on
each track. Typical is 35 sectors per track, high capacity drives I
have seen were up to 54 sectors/track.
Although ESDI has been largely obsoleted by IDE and SCSI
interfaces, the availability of free or cheap surplus drives makes
them ideal for low (or now) budget systems.
Concepts of ESDI
Physical connections
The ESDI interface uses two cables connected to each drive.
One cable is a 34 pin flat cable edge connector that carries the
command and status signals from the controller to the drive and
vice-versa. The command cable is daisy chained between all the
drives. So, it forms a bus onto which all drives are
connected.
The second cable is a 20 pin flat cable edge connector that
carries the data to and from the drive. This cable is radially
connected, so each drive has its own direct connection to the
controller.
To the best of my knowledge PC ESDI controllers are limited to
using a maximum of 2 drives per controller. This is compatibility
feature(?) left over from the WD1003 standard that reserves only a
single bit for device addressing.
Device addressing
On each command cable a maximum of 7 devices and 1 controller
can be present. To enable the controller to uniquely identify
which drive it addresses, each ESDI device is equipped with
jumpers or switches to select the devices address.
On PC type controllers the first drive is set to address 0,
the second disk to address 1. Always make
sure you set each disk to an unique address! So, on a
PC with its two drives/controller maximum the first drive is drive
0, the second is drive 1.
Termination
The daisy chained command cable (the 34 pin cable remember?)
needs to be terminated at the last drive on the chain. For this
purpose ESDI drives come with a termination resistor network that
can be removed or disabled by a jumper when it is not used.
So, one and only one drive, the one at
the farthest end of the command cable has its terminator
installed/enabled. The controller automatically terminates the
other end of the cable. Please note that this implies that the
controller must be at one end of the cable and
not in the middle.
Using ESDI disks with FreeBSD
Why is ESDI such a pain to get working in the first
place?
People who tried ESDI disks with FreeBSD are known to have
developed a profound sense of frustration. A combination of factors
works against you to produce effects that are hard to understand
when you have never seen them before.
This has also led to the popular legend ESDI and FreeBSD is a
plain NO-GO. The following sections try to list all the pitfalls
and solutions.
ESDI speed variants
As briefly mentioned before, ESDI comes in two speed flavors.
The older drives and controllers use a 10 Mbits/second data
transfer rate. Newer stuff uses 15 Mbits/second.
It is not hard to imagine that 15 Mbits/second drive cause
problems on controllers laid out for 10 Mbits/second. As always,
consult your controller and drive
documentation to see if things match.
Stay on track
Mainstream ESDI drives use 34 to 36 sectors per track. Most
(older) controllers cannot handle more than this number of
sectors. Newer, higher capacity, drives use higher numbers of
sectors per track. For instance, I own a 670 MB drive that has 54
sectors per track.
In my case, the controller could not handle this number of
sectors. It proved to work well except that it only used 35
sectors on each track. This meant losing a lot of disk
space.
Once again, check the documentation of your hardware for more
info. Going out-of-spec like in the example might or might not
work. Give it a try or get another more capable
controller.
Hard or soft sectoring
Most ESDI drives allow hard or soft sectoring to be selected
using a jumper. Hard sectoring means that the drive will produce
a sector pulse on the start of each new sector. The controller
uses this pulse to tell when it should start to write or
read.
Hard sectoring allows a selection of sector size (normally
256, 512 or 1024 bytes per formatted sector). FreeBSD uses 512
byte sectors. The number of sectors per track also varies while
still using the same number of bytes per formatted sector. The
number of unformatted bytes per sector
varies, dependent on your controller it needs more or less
overhead bytes to work correctly. Pushing more sectors on a
track of course gives you more usable space, but might give
problems if your controller needs more bytes than the drive
offers.
In case of soft sectoring, the controller itself determines
where to start/stop reading or writing. For ESDI hard sectoring
is the default (at least on everything I came across). I never
felt the urge to try soft sectoring.
In general, experiment with sector settings before you install
FreeBSD because you need to re-run the low-level format after each
change.
Low level formatting
ESDI drives need to be low level formatted before they are
usable. A reformat is needed whenever you figgle with the number
of sectors/track jumpers or the physical orientation of the drive
(horizontal, vertical). So, first think, then format. The format
time must not be underestimated, for big disks it can take
hours.
After a low level format, a surface scan is done to find and
flag bad sectors. Most disks have a manufacturer bad block list
listed on a piece of paper or adhesive sticker. In addition, on
most disks the list is also written onto the disk. Please use the
manufacturer's list. It is much easier to remap a defect now than
after FreeBSD is installed.
Stay away from low-level formatters that mark all sectors of a
track as bad as soon as they find one bad sector. Not only does
this waste space, it also and more importantly causes you grief
with bad144 (see the section on bad144).
Translations
Translations, although not exclusively a ESDI-only problem,
might give you real trouble. Translations come in multiple
flavors. Most of them have in common that they attempt to work
around the limitations posed upon disk geometries by the original
IBM PC/AT design (thanks IBM!).
First of all there is the (in)famous 1024 cylinder limit. For
a system to be able to boot, the stuff (whatever operating system)
must be in the first 1024 cylinders of a disk. Only 10 bits are
available to encode the cylinder number. For the number of
sectors the limit is 64 (0-63). When you combine the 1024
cylinder limit with the 16 head limit (also a design feature) you
max out at fairly limited disk sizes.
To work around this problem, the manufacturers of ESDI PC
controllers added a BIOS prom extension on their boards. This
BIOS extension handles disk I/O for booting (and for some
operating systems all disk I/O) by using
translation. For instance, a big drive might be presented to the
system as having 32 heads and 64 sectors/track. The result is
that the number of cylinders is reduced to something below 1024
and is therefore usable by the system without problems. It is
noteworthy to know that FreeBSD does not use the BIOS after its
kernel has started. More on this later.
A second reason for translations is the fact that most older
system BIOSes could only handle drives with 17 sectors per track
(the old ST412 standard). Newer system BIOSes usually have a
user-defined drive type (in most cases this is drive type
47).
Whatever you do to translations after reading this document,
keep in mind that if you have multiple operating systems on the
same disk, all must use the same translation
While on the subject of translations, I have seen one
controller type (but there are probably more like this) offer the
option to logically split a drive in multiple partitions as a BIOS
option. I had select 1 drive == 1 partition because this
controller wrote this info onto the disk. On power-up it read the
info and presented itself to the system based on the info from the
disk.
Spare sectoring
Most ESDI controllers offer the possibility to remap bad
sectors. During/after the low-level format of the disk bad
sectors are marked as such, and a replacement sector is put in
place (logically of course) of the bad one.
In most cases the remapping is done by using N-1 sectors on
each track for actual data storage, and sector N itself is the
spare sector. N is the total number of sectors physically
available on the track. The idea behind this is that the
operating system sees a 'perfect' disk without bad sectors. In
the case of FreeBSD this concept is not usable.
The problem is that the translation from
bad to good is performed
by the BIOS of the ESDI controller. FreeBSD, being a true 32 bit
operating system, does not use the BIOS after it has been booted.
Instead, it has device drivers that talk directly to the
hardware.
So: don't use spare sectoring, bad block remapping
or whatever it may be called by the controller manufacturer when
you want to use the disk for FreeBSD.
Bad block handling
The preceding section leaves us with a problem. The
controller's bad block handling is not usable and still FreeBSD's
filesystems assume perfect media without any flaws. To solve this
problem, FreeBSD use the bad144 tool. Bad144
(named after a Digital Equipment standard for bad block handling)
scans a FreeBSD slice for bad blocks. Having found these bad
blocks, it writes a table with the offending block numbers to the
end of the FreeBSD slice.
When the disk is in operation, the disk accesses are checked
against the table read from the disk. Whenever a block number is
requested that is in the bad144 list, a
replacement block (also from the end of the FreeBSD slice) is
used. In this way, the bad144 replacement
scheme presents 'perfect' media to the FreeBSD filesystems.
There are a number of potential pitfalls associated with the
use of bad144 . First of all, the slice cannot
have more than 126 bad sectors. If your drive has a high number
of bad sectors, you might need to divide it into multiple FreeBSD
slices each containing less than 126 bad sectors. Stay away from
low-level format programs that mark every
sector of a track as bad when they find a flaw on the track. As
you can imagine, the 126 limit is quickly reached when the
low-level format is done this way.
Second, if the slice contains the root filesystem, the slice
should be within the 1024 cylinder BIOS limit. During the boot
process the bad144 list is read using the BIOS and this only
succeeds when the list is within the 1024 cylinder limit.
The restriction is not that only the root
filesystem must be within the 1024 cylinder
limit, but rather the entire slice that
contains the root filesystem.
Kernel configuration
ESDI disks are handled by the same wd driver
as IDE and ST412 MFM disks. The wd driver
should work for all WD1003 compatible interfaces.
Most hardware is jumperable for one of two different I/O
address ranges and IRQ lines. This allows you to have two wd
type controllers in one system.
When your hardware allows non-standard strappings, you can use
these with FreeBSD as long as you enter the correct info into the
kernel config file. An example from the kernel config file (they
live in /sys/i386/conf BTW).
# First WD compatible controller
controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr
disk wd0 at wdc0 drive 0
disk wd1 at wdc0 drive 1
# Second WD compatible controller
controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr
disk wd2 at wdc1 drive 0
disk wd3 at wdc1 drive 1
Particulars on ESDI hardware
Adaptec 2320 controllers
I successfully installed FreeBSD onto a ESDI disk controlled
by a ACB-2320. No other operating system was present on the
disk.
To do so I low level formatted the disk using
NEFMT.EXE (ftp able from
www.adaptec.com ) and answered NO to
the question whether the disk should be formatted with a spare
sector on each track. The BIOS on the ACD-2320 was disabled. I
used the free configurable option in the system
BIOS to allow the BIOS to boot it.
Before using NEFMT.EXE I tried to format
the disk using the ACB-2320 BIOS built-in formatter. This proved
to be a show stopper, because it did not give me an option to
disable spare sectoring. With spare sectoring enabled the FreeBSD
installation process broke down on the bad144
run.
Please check carefully which
ACB-232xy variant you have. The
x is either 0 or
2 , indicating a controller without or with a
floppy controller on board.
The y is more interesting. It can either
be a blank, a A-8 or a D . A
blank indicates a plain 10 Mbits/second controller. An
A-8 indicates a 15 Mbits/second controller
capable of handling 52 sectors/track. A D
means a 15 Mbits/second controller that can also handle drives
with > 36 sectors/track (also 52 ?).
All variations should be capable of using 1:1 interleaving.
Use 1:1, FreeBSD is fast enough to handle it.
Western Digital WD1007 controllers
I successfully installed FreeBSD onto a ESDI disk controlled
by a WD1007 controller. To be precise, it was a WD1007-WA2.
Other variations of the WD1007 do exist.
To get it to work, I had to disable the sector translation and
the WD1007's onboard BIOS. This implied I could not use the
low-level formatter built into this BIOS. Instead, I grabbed
WDFMT.EXE from www.wdc.com Running this formatted my drive
just fine.
Ultrastor U14F controllers
According to multiple reports from the net, Ultrastor ESDI
boards work OK with FreeBSD. I lack any further info on
particular settings.
Further reading
If you intend to do some serious ESDI hacking, you might want to
have the official standard at hand:
The latest ANSI X3T10 committee document is: Enhanced Small
Device Interface (ESDI) [X3.170-1990/X3.170a-1991] [X3T10/792D
Rev 11]
On Usenet the newsgroup comp.periphs is a noteworthy place
to look for more info.
The World Wide Web (WWW) also proves to be a very handy info
source: For info on Adaptec ESDI controllers see http://www.adaptec.com/ . For
info on Western Digital controllers see http://www.wdc.com/ .
Thanks to...
Andrew Gordon for sending me an Adaptec 2320 controller and ESDI
disk for testing.
What is SCSI?
Copyright © 1995, &a.wilko;. July 6,
1996.
SCSI is an acronym for Small Computer Systems Interface. It is an
ANSI standard that has become one of the leading I/O buses in the
computer industry. The foundation of the SCSI standard was laid by
Shugart Associates (the same guys that gave the world the first mini
floppy disks) when they introduced the SASI bus (Shugart Associates
Standard Interface).
After some time an industry effort was started to come to a more
strict standard allowing devices from different vendors to work
together. This effort was recognized in the ANSI SCSI-1 standard.
The SCSI-1 standard (approximately 1985) is rapidly becoming obsolete. The
current standard is SCSI-2 (see Further reading), with SCSI-3
on the drawing boards.
In addition to a physical interconnection standard, SCSI defines a
logical (command set) standard to which disk devices must adhere.
This standard is called the Common Command Set (CCS) and was developed
more or less in parallel with ANSI SCSI-1. SCSI-2 includes the
(revised) CCS as part of the standard itself. The commands are
dependent on the type of device at hand. It does not make much sense
of course to define a Write command for a scanner.
The SCSI bus is a parallel bus, which comes in a number of
variants. The oldest and most used is an 8 bit wide bus, with
single-ended signals, carried on 50 wires. (If you do not know what
single-ended means, do not worry, that is what this document is all
about.) Modern designs also use 16 bit wide buses, with differential
signals. This allows transfer speeds of 20Mbytes/second, on cables
lengths of up to 25 meters. SCSI-2 allows a maximum bus width of 32
bits, using an additional cable. Quickly emerging are Ultra SCSI (also
called Fast-20) and Ultra2 (also called Fast-40). Fast-20 is 20
million transfers per second (20 Mbytes/sec on a 8 bit bus), Fast-40
is 40 million transfers per second (40 Mbytes/sec on a 8 bit bus).
Most hard drives sold today are single-ended Ultra SCSI (8 or 16
bits).
Of course the SCSI bus not only has data lines, but also a number
of control signals. A very elaborate protocol is part of the standard
to allow multiple devices to share the bus in an efficient manner. In
SCSI-2, the data is always checked using a separate parity line. In
pre-SCSI-2 designs parity was optional.
In SCSI-3 even faster bus types are introduced, along with a
serial SCSI busses that reduces the cabling overhead and allows a
higher maximum bus length. You might see names like SSA and
fibre channel in this context. None of the serial buses are currently
in widespread use (especially not in the typical FreeBSD environment).
For this reason the serial bus types are not discussed any
further.
As you could have guessed from the description above, SCSI devices
are intelligent. They have to be to adhere to the SCSI standard
(which is over 2 inches thick BTW). So, for a hard disk drive for
instance you do not specify a head/cylinder/sector to address a
particular block, but simply the number of the block you want.
Elaborate caching schemes, automatic bad block replacement etc are all
made possible by this 'intelligent device' approach.
On a SCSI bus, each possible pair of devices can communicate.
Whether their function allows this is another matter, but the standard
does not restrict it. To avoid signal contention, the 2 devices have
to arbitrate for the bus before using it.
The philosophy of SCSI is to have a standard that allows
older-standard devices to work with newer-standard ones. So, an old
SCSI-1 device should normally work on a SCSI-2 bus. I say Normally,
because it is not absolutely sure that the implementation of an old
device follows the (old) standard closely enough to be acceptable on a
new bus. Modern devices are usually more well-behaved, because the
standardization has become more strict and is better adhered to by the
device manufacturers.
Generally speaking, the chances of getting a working set of
devices on a single bus is better when all the devices are SCSI-2 or
newer. This implies that you do not have to dump all your old stuff
when you get that shiny 2GB disk: I own a system on which a pre-SCSI-1
disk, a SCSI-2 QIC tape unit, a SCSI-1 helical scan tape unit and 2
SCSI-1 disks work together quite happily. From a performance
standpoint you might want to separate your older and newer (=faster)
devices however.
Components of SCSI
As said before, SCSI devices are smart. The idea is to put the
knowledge about intimate hardware details onto the SCSI device
itself. In this way, the host system does not have to worry about
things like how many heads a hard disks has, or how many tracks
there are on a specific tape device. If you are curious, the
standard specifies commands with which you can query your devices on
their hardware particulars. FreeBSD uses this capability during
boot to check out what devices are connected and whether they need
any special treatment.
The advantage of intelligent devices is obvious: the device
drivers on the host can be made in a much more generic fashion,
there is no longer a need to change (and qualify!) drivers for every
odd new device that is introduced.
For cabling and connectors there is a golden rule: get good
stuff. With bus speeds going up all the time you will save yourself
a lot of grief by using good material.
So, gold plated connectors, shielded cabling, sturdy connector
hoods with strain reliefs etc are the way to go. Second golden rule:
do no use cables longer than necessary. I once spent 3 days hunting
down a problem with a flaky machine only to discover that shortening
the SCSI bus by 1 meter solved the problem. And the original bus
length was well within the SCSI specification.
SCSI bus types
From an electrical point of view, there are two incompatible bus
types: single-ended and differential. This means that there are two
different main groups of SCSI devices and controllers, which cannot
be mixed on the same bus. It is possible however to use special
converter hardware to transform a single-ended bus into a
differential one (and vice versa). The differences between the bus
types are explained in the next sections.
In lots of SCSI related documentation there is a sort of jargon
in use to abbreviate the different bus types. A small list:
FWD: Fast Wide Differential
FND: Fast Narrow Differential
SE: Single Ended
FN: Fast Narrow
etc.
With a minor amount of imagination one can usually imagine what
is meant.
Wide is a bit ambiguous, it can indicate 16 or 32 bit buses. As
far as I know, the 32 bit variant is not (yet) in use, so wide
normally means 16 bit.
Fast means that the timing on the bus is somewhat different, so
that on a narrow (8 bit) bus 10 Mbytes/sec are possible instead of 5
Mbytes/sec for 'slow' SCSI. As discussed before, bus speeds of 20
and 40 million transfers/second are also emerging (Fast-20 == Ultra
SCSI and Fast-40 == Ultra2 SCSI).
The data lines > 8 are only used for data transfers and
device addressing. The transfers of commands and status messages
etc are only performed on the lowest 8 data lines. The standard
allows narrow devices to operate on a wide bus. The usable bus
width is negotiated between the devices. You have to watch your
device addressing closely when mixing wide and narrow.
Single ended buses
A single-ended SCSI bus uses signals that are either 5 Volts
or 0 Volts (indeed, TTL levels) and are relative to a COMMON
ground reference. A singled ended 8 bit SCSI bus has
approximately 25 ground lines, who are all tied to a single `rail'
on all devices. A standard single ended bus has a maximum length
of 6 meters. If the same bus is used with fast-SCSI devices, the
maximum length allowed drops to 3 meters. Fast-SCSI means that
instead of 5Mbytes/sec the bus allows 10Mbytes/sec
transfers.
Fast-20 (Ultra SCSI) and Fast-40 allow for 20 and 40 million
transfers/second respectively. So, F20 is 20 Mbytes/second on a 8
bit bus, 40 Mbytes/second on a 16 bit bus etc. For F20 the max
bus length is 1.5 meters, for F40 it becomes 0.75 meters. Be
aware that F20 is pushing the limits quite a bit, so you will
quickly find out if your SCSI bus is electrically sound.
If some devices on your bus use 'fast' to communicate your
bus must adhere to the length restrictions for fast
buses!
It is obvious that with the newer fast-SCSI devices the bus
length can become a real bottleneck. This is why the differential
SCSI bus was introduced in the SCSI-2 standard.
For connector pinning and connector types please refer to the
SCSI-2 standard (see Further
reading) itself, connectors etc are listed there in
painstaking detail.
Beware of devices using non-standard cabling. For instance
Apple uses a 25pin D-type connecter (like the one on serial ports
and parallel printers). Considering that the official SCSI bus
needs 50 pins you can imagine the use of this connector needs some
'creative cabling'. The reduction of the number of ground wires
they used is a bad idea, you better stick to 50 pins cabling in
accordance with the SCSI standard. For Fast-20 and 40 do not even
think about buses like this.
Differential buses
A differential SCSI bus has a maximum length of 25 meters.
Quite a difference from the 3 meters for a single-ended fast-SCSI
bus. The idea behind differential signals is that each bus signal
has its own return wire. So, each signal is carried on a
(preferably twisted) pair of wires. The voltage difference
between these two wires determines whether the signal is asserted
or de-asserted. To a certain extent the voltage difference
between ground and the signal wire pair is not relevant (do not
try 10 kVolts though).
It is beyond the scope of this document to explain why this
differential idea is so much better. Just accept that
electrically seen the use of differential signals gives a much
better noise margin. You will normally find differential buses in
use for inter-cabinet connections. Because of the lower cost
single ended is mostly used for shorter buses like inside
cabinets.
There is nothing that stops you from using differential stuff
with FreeBSD, as long as you use a controller that has device
driver support in FreeBSD. As an example, Adaptec marketed the
AHA1740 as a single ended board, whereas the AHA1744 was
differential. The software interface to the host is identical for
both.
Terminators
Terminators in SCSI terminology are resistor networks that are
used to get a correct impedance matching. Impedance matching is
important to get clean signals on the bus, without reflections or
ringing. If you once made a long distance telephone call on a bad
line you probably know what reflections are. With 20Mbytes/sec
traveling over your SCSI bus, you do not want signals echoing
back.
Terminators come in various incarnations, with more or less
sophisticated designs. Of course, there are internal and external
variants. Many SCSI devices come with a number of sockets in
which a number of resistor networks can (must be!) installed. If
you remove terminators from a device, carefully store them. You
will need them when you ever decide to reconfigure your SCSI bus.
There is enough variation in even these simple tiny things to make
finding the exact replacement a frustrating business. There are
also SCSI devices that have a single jumper to enable or disable a
built-in terminator. There are special terminators you can stick
onto a flat cable bus. Others look like external connectors, or a
connector hood without a cable. So, lots of choice as you can
see.
There is much debate going on if and when you should switch
from simple resistor (passive) terminators to active terminators.
Active terminators contain slightly more elaborate circuit to give
cleaner bus signals. The general consensus seems to be that the
usefulness of active termination increases when you have long
buses and/or fast devices. If you ever have problems with your
SCSI buses you might consider trying an active terminator. Try to
borrow one first, they reputedly are quite expensive.
Please keep in mind that terminators for differential and
single-ended buses are not identical. You should not
mix the two variants.
OK, and now where should you install your terminators? This is
by far the most misunderstood part of SCSI. And it is by far the
simplest. The rule is: every single line on the SCSI
bus has 2 (two) terminators, one at each end of the
bus. So, two and not one or three or whatever. Do
yourself a favor and stick to this rule. It will save you endless
grief, because wrong termination has the potential to introduce
highly mysterious bugs. (Note the potential
here;
the nastiest part is that it may or may not work.)
A common pitfall is to have an internal (flat) cable in a
machine and also an external cable attached to the controller. It
seems almost everybody forgets to remove the terminators from the
controller. The terminator must now be on the last external
device, and not on the controller! In general, every
reconfiguration of a SCSI bus must pay attention to this.
Termination is to be done on a per-line basis. This means
if you have both narrow and wide buses connected to the same
host adapter, you need to enable termination on the higher 8
bits of the bus on the adapter (as well as the last devices on
each bus, of course).
What I did myself is remove all terminators from my SCSI
devices and controllers. I own a couple of external terminators,
for both the Centronics-type external cabling and for the internal
flat cable connectors. This makes reconfiguration much
easier.
On modern devices, sometimes integrated terminators are used.
These things are special purpose integrated circuits that can be
enabled or disabled with a control pin. It is not necessary to
physically remove them from a device. You may find them on newer
host adapters, sometimes they are software configurable, using
some sort of setup tool. Some will even auto-detect the cables
attached to the connectors and automatically set up the
termination as necessary. At any rate, consult your
documentation!
Terminator power
The terminators discussed in the previous chapter need power
to operate properly. On the SCSI bus, a line is dedicated to this
purpose. So, simple huh?
Not so. Each device can provide its own terminator power to
the terminator sockets it has on-device. But if you have external
terminators, or when the device supplying the terminator power to
the SCSI bus line is switched off you are in trouble.
The idea is that initiators (these are devices that initiate
actions on the bus, a discussion follows) must supply terminator
power. All SCSI devices are allowed (but not required) to supply
terminator power.
To allow for un-powered devices on a bus, the terminator power
must be supplied to the bus via a diode. This prevents the
backflow of current to un-powered devices.
To prevent all kinds of nastiness, the terminator power is
usually fused. As you can imagine, fuses might blow. This can,
but does not have to, lead to a non functional bus. If multiple
devices supply terminator power, a single blown fuse will not put
you out of business. A single supplier with a blown fuse
certainly will. Clever external terminators sometimes have a LED
indication that shows whether terminator power is present.
In newer designs auto-restoring fuses that 'reset' themselves
after some time are sometimes used.
Device addressing
Because the SCSI bus is, ehh, a bus there must be a way to
distinguish or address the different devices connected to
it.
This is done by means of the SCSI or target ID. Each device
has a unique target ID. You can select the ID to which a device
must respond using a set of jumpers, or a dip switch, or something
similar. Some SCSI host adapters let you change the target ID
from the boot menu. (Yet some others will not let you change the
ID from 7.) Consult the documentation of your device for more
information.
Beware of multiple devices configured to use the same ID.
Chaos normally reigns in this case. A pitfall is that one of the
devices sharing the same ID sometimes even manages to answer to
I/O requests!
For an 8 bit bus, a maximum of 8 targets is possible. The
maximum is 8 because the selection is done bitwise using the 8
data lines on the bus. For wide buses this increases to the
number of data lines (usually 16).
A narrow SCSI device can not communicate with a SCSI device
with a target ID larger than 7. This means it is generally not
a good idea to move your SCSI host adapter's target ID to
something higher than 7 (or your CDROM will stop
working).
The higher the SCSI target ID, the higher the priority the
devices has. When it comes to arbitration between devices that
want to use the bus at the same time, the device that has the
highest SCSI ID will win. This also means that the SCSI host
adapter usually uses target ID 7. Note however that the lower 8
IDs have higher priorities than the higher 8 IDs on a wide-SCSI
bus. Thus, the order of target IDs is: [7 6 .. 1 0 15 14 .. 9 8]
- on a wide-SCSI system. (If you you are wondering why the lower 8
+ on a wide-SCSI system. (If you are wondering why the lower 8
have higher priority, read the previous paragraph for a
hint.)
For a further subdivision, the standard allows for Logical
Units or LUNs for short. A single target ID may have multiple
LUNs. For example, a tape device including a tape changer may
have LUN 0 for the tape device itself, and LUN 1 for the tape
changer. In this way, the host system can address each of the
functional units of the tape changer as desired.
Bus layout
SCSI buses are linear. So, not shaped like Y-junctions, star
topologies, rings, cobwebs or whatever else people might want to
invent. One of the most common mistakes is for people with
wide-SCSI host adapters to connect devices on all three connecters
(external connector, internal wide connector, internal narrow
connector). Don't do that. It may appear to work if you are
really lucky, but I can almost guarantee that your system will
stop functioning at the most unfortunate moment (this is also
known as Murphy's law
).
You might notice that the terminator issue discussed earlier
becomes rather hairy if your bus is not linear. Also, if you have
more connectors than devices on your internal SCSI cable, make
sure you attach devices on connectors on both ends instead of
using the connectors in the middle and let one or both ends
dangle. This will screw up the termination of the bus.
The electrical characteristics, its noise margins and
ultimately the reliability of it all are tightly related to linear
bus rule.
Stick to the linear bus rule!
Using SCSI with FreeBSD
About translations, BIOSes and magic...
As stated before, you should first make sure that you have a
electrically sound bus.
When you want to use a SCSI disk on your PC as boot disk, you
must aware of some quirks related to PC BIOSes. The PC BIOS in
its first incarnation used a low level physical interface to the
hard disk. So, you had to tell the BIOS (using a setup tool or a
BIOS built-in setup) how your disk physically looked like. This
involved stating number of heads, number of cylinders, number of
sectors per track, obscure things like precompensation and reduced
write current cylinder etc.
One might be inclined to think that since SCSI disks are smart
you can forget about this. Alas, the arcane setup issue is still
present today. The system BIOS needs to know how to access your
SCSI disk with the head/cyl/sector method in order to load the
FreeBSD kernel during boot.
The SCSI host adapter or SCSI controller you have put in your
AT/EISA/PCI/whatever bus to connect your disk therefore has its
own on-board BIOS. During system startup, the SCSI BIOS takes
over the hard disk interface routines from the system BIOS. To
fool the system BIOS, the system setup is normally set to No hard
disk present. Obvious, isn't it?
The SCSI BIOS itself presents to the system a so called
translated drive. This means that a fake
drive table is constructed that allows the PC to boot the drive.
This translation is often (but not always) done using a pseudo
drive with 64 heads and 32 sectors per track. By varying the
number of cylinders, the SCSI BIOS adapts to the actual drive
size. It is useful to note that 32 * 64 / 2 = the size of your
drive in megabytes. The division by 2 is to get from disk blocks
that are normally 512 bytes in size to Kbytes.
Right. All is well now?! No, it is not. The system BIOS has
another quirk you might run into. The number of cylinders of a
bootable hard disk cannot be greater than 1024. Using the
translation above, this is a show-stopper for disks greater than 1
GB. With disk capacities going up all the time this is causing
problems.
Fortunately, the solution is simple: just use another
translation, e.g. with 128 heads instead of 32. In most cases new
SCSI BIOS versions are available to upgrade older SCSI host
adapters. Some newer adapters have an option, in the form of a
jumper or software setup selection, to switch the translation the
SCSI BIOS uses.
It is very important that all operating
systems on the disk use the same translation
to get the right idea about where to find the relevant partitions.
So, when installing FreeBSD you must answer any questions about
heads/cylinders etc using the translated values your host adapter
uses.
Failing to observe the translation issue might lead to
un-bootable systems or operating systems overwriting each others
partitions. Using fdisk you should be able to see all
partitions.
You might have heard some talk of lying
devices?
Older FreeBSD kernels used to report the geometry of SCSI disks
when booting. An example from one of my systems:
aha0 targ 0 lun 0: <MICROP 1588-15MB1057404HSP4>
sd0: 636MB (1303250 total sec), 1632 cyl, 15 head, 53 sec, bytes/sec 512
Newer kernels usually do not report this information.
e.g.
(bt0:0:0): "SEAGATE ST41651 7574" type 0 fixed SCSI 2
sd0(bt0:0:0): Direct-Access 1350MB (2766300 512 byte sectors)
Why has this changed?
This info is retrieved from the SCSI disk itself. Newer disks
often use a technique called zone bit recording. The idea is that
on the outer cylinders of the drive there is more space so more
sectors per track can be put on them. This results in disks that
have more tracks on outer cylinders than on the inner cylinders
and, last but not least, have more capacity. You can imagine that
the value reported by the drive when inquiring about the geometry
now becomes suspect at best, and nearly always misleading. When
asked for a geometry , it is nearly always better to supply the
geometry used by the BIOS, or if the BIOS is never going
to know about this disk , (e.g. it is not a booting
disk) to supply a fictitious geometry that is convenient.
SCSI subsystem design
FreeBSD uses a layered SCSI subsystem. For each different
controller card a device driver is written. This driver knows all
the intimate details about the hardware it controls. The driver
has a interface to the upper layers of the SCSI subsystem through
which it receives its commands and reports back any status.
On top of the card drivers there are a number of more generic
drivers for a class of devices. More specific: a driver for tape
devices (abbreviation: st), magnetic disks (sd), CDROMs (cd) etc.
In case you are wondering where you can find this stuff, it all
lives in /sys/scsi . See the man pages in
section 4 for more details.
The multi level design allows a decoupling of low-level bit
banging and more high level stuff. Adding support for another
piece of hardware is a much more manageable problem.
Kernel configuration
Dependent on your hardware, the kernel configuration file must
contain one or more lines describing your host adapter(s). This
includes I/O addresses, interrupts etc. Consult the man page for
your adapter driver to get more info. Apart from that, check out
/sys/i386/conf/LINT for an overview of a
kernel config file. LINT contains every
possible option you can dream of. It does
not imply LINT will
actually get you to a working kernel at all.
Although it is probably stating the obvious: the kernel config
file should reflect your actual hardware setup. So, interrupts,
I/O addresses etc must match the kernel config file. During
system boot messages will be displayed to indicate whether the
configured hardware was actually found.
Note that most of the EISA/PCI drivers (namely
ahb , ahc ,
ncr and amd
will automatically obtain the correct parameters from the host
adapters themselves at boot time; thus, you just need to write,
for instance, controller ahc0 .
An example loosely based on the FreeBSD 2.2.5-Release kernel
config file LINT with some added comments
(between []):
# SCSI host adapters: `aha', `ahb', `aic', `bt', `nca'
#
# aha: Adaptec 154x
# ahb: Adaptec 174x
# ahc: Adaptec 274x/284x/294x
# aic: Adaptec 152x and sound cards using the Adaptec AIC-6360 (slow!)
# amd: AMD 53c974 based SCSI cards (e.g., Tekram DC-390 and 390T)
# bt: Most Buslogic controllers
# nca: ProAudioSpectrum cards using the NCR 5380 or Trantor T130
# ncr: NCR/Symbios 53c810/815/825/875 etc based SCSI cards
# uha: UltraStore 14F and 34F
# sea: Seagate ST01/02 8 bit controller (slow!)
# wds: Western Digital WD7000 controller (no scatter/gather!).
#
[For an Adaptec AHA274x/284x/294x/394x etc controller]
controller ahc0
[For an NCR/Symbios 53c875 based controller]
controller ncr0
[For an Ultrastor adapter]
controller uha0 at isa? port "IO_UHA0" bio irq ? drq 5 vector uhaintr
# Map SCSI buses to specific SCSI adapters
controller scbus0 at ahc0
controller scbus2 at ncr0
controller scbus1 at uha0
# The actual SCSI devices
disk sd0 at scbus0 target 0 unit 0 [SCSI disk 0 is at scbus 0, LUN 0]
disk sd1 at scbus0 target 1 [implicit LUN 0 if omitted]
disk sd2 at scbus1 target 3 [SCSI disk on the uha0]
disk sd3 at scbus2 target 4 [SCSI disk on the ncr0]
tape st1 at scbus0 target 6 [SCSI tape at target 6]
device cd0 at scbus? [the first ever CDROM found, no wiring]
The example above tells the kernel to look for a ahc (Adaptec
274x) controller, then for an NCR/Symbios board, and so on. The
lines following the controller specifications tell the kernel to
configure specific devices but only attach
them when they match the target ID and LUN specified on the
corresponding bus.
Wired down devices get first shot
at the unit
numbers so the first non wired down
device, is
allocated the unit number one greater than the highest
wired down
unit number for that kind of device. So,
if you had a SCSI tape at target ID 2 it would be configured as
st2, as the tape at target ID 6 is wired down to unit number
1.
Wired down devices need not be found to get their unit
number. The unit number for a wired down device is reserved for
that device, even if it is turned off at boot time. This allows
the device to be turned on and brought on-line at a later time,
without rebooting. Notice that a device's unit number has
no relationship with its target ID on the
SCSI bus.
Below is another example of a kernel config file as used by
FreeBSD version < 2.0.5. The difference with the first example
is that devices are not wired down
. Wired
down
means that you specify which SCSI target belongs to
which device.
A kernel built to the config file below will attach the first
SCSI disk it finds to sd0, the second disk to sd1 etc. If you ever
removed or added a disk, all other devices of the same type (disk
in this case) would 'move around'. This implies you have to
change /etc/fstab each time.
Although the old style still works, you are
strongly recommended to use this new feature.
It will save you a lot of grief whenever you shift your hardware
around on the SCSI buses. So, when you re-use your old trusty
config file after upgrading from a pre-FreeBSD2.0.5.R system check
this out.
[driver for Adaptec 174x]
controller ahb0 at isa? bio irq 11 vector ahbintr
[for Adaptec 154x]
controller aha0 at isa? port "IO_AHA0" bio irq 11 drq 5 vector ahaintr
[for Seagate ST01/02]
controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr
controller scbus0
device sd0 [support for 4 SCSI harddisks, sd0 up sd3]
device st0 [support for 2 SCSI tapes]
[for the CDROM]
device cd0 #Only need one of these, the code dynamically grows
Both examples support SCSI disks. If during boot more devices
of a specific type (e.g. sd disks) are found than are configured
in the booting kernel, the system will simply allocate more
devices, incrementing the unit number starting at the last number
wired down
. If there are no wired
down
devices then counting starts at unit 0.
Use man 4 scsi to check for the latest info
on the SCSI subsystem. For more detailed info on host adapter
drivers use e.g., man 4 ahc for info on the
Adaptec 294x driver.
Tuning your SCSI kernel setup
Experience has shown that some devices are slow to respond to
INQUIRY commands after a SCSI bus reset (which happens at boot
time). An INQUIRY command is sent by the kernel on boot to see
what kind of device (disk, tape, CDROM etc.) is connected to a
specific target ID. This process is called device probing by the
way.
To work around the 'slow response' problem, FreeBSD allows a
tunable delay time before the SCSI devices are probed following a
SCSI bus reset. You can set this delay time in your kernel
configuration file using a line like:
options SCSI_DELAY=15 #Be pessimistic about Joe SCSI device
This line sets the delay time to 15 seconds. On my own system
I had to use 3 seconds minimum to get my trusty old CDROM drive
to be recognized. Start with a high value (say 30 seconds or so)
when you have problems with device recognition. If this helps,
tune it back until it just stays working.
Rogue SCSI devices
Although the SCSI standard tries to be complete and concise,
it is a complex standard and implementing things correctly is no
easy task. Some vendors do a better job then others.
This is exactly where the rogue
devices come
into view. Rogues are devices that are recognized by the FreeBSD
kernel as behaving slightly (...) non-standard. Rogue devices are
reported by the kernel when booting. An example for two of my
cartridge tape units:
Feb 25 21:03:34 yedi /kernel: ahb0 targ 5 lun 0: <TANDBERG TDC 3600 -06:>
Feb 25 21:03:34 yedi /kernel: st0: Tandberg tdc3600 is a known rogue
Mar 29 21:16:37 yedi /kernel: aha0 targ 5 lun 0: <ARCHIVE VIPER 150 21247-005>
Mar 29 21:16:37 yedi /kernel: st1: Archive Viper 150 is a known rogue
For instance, there are devices that respond to all LUNs on a
certain target ID, even if they are actually only one device. It
is easy to see that the kernel might be fooled into believing that
there are 8 LUNs at that particular target ID. The confusion this
causes is left as an exercise to the reader.
The SCSI subsystem of FreeBSD recognizes devices with bad
habits by looking at the INQUIRY response they send when probed.
Because the INQUIRY response also includes the version number of
the device firmware, it is even possible that for different
firmware versions different workarounds are used. See e.g.
/sys/scsi/st.c and
/sys/scsi/scsiconf.c for more info on how
this is done.
This scheme works fine, but keep in mind that it of course
only works for devices that are known to be weird. If you are the
first to connect your bogus Mumbletech SCSI CDROM you might be
the one that has to define which workaround is needed.
After you got your Mumbletech working, please send the
required workaround to the FreeBSD development team for inclusion
in the next release of FreeBSD. Other Mumbletech owners will be
grateful to you.
Multiple LUN devices
In some cases you come across devices that use multiple
logical units (LUNs) on a single SCSI ID. In most cases FreeBSD
only probes devices for LUN 0. An example are so called bridge
boards that connect 2 non-SCSI harddisks to a SCSI bus (e.g. an
Emulex MD21 found in old Sun systems).
This means that any devices with LUNs != 0 are not normally
found during device probe on system boot. To work around this
problem you must add an appropriate entry in /sys/scsi/scsiconf.c
and rebuild your kernel.
Look for a struct that is initialized like below:
{
T_DIRECT, T_FIXED, "MAXTOR", "XT-4170S", "B5A",
"mx1", SC_ONE_LU
}
For you Mumbletech BRIDGE2000 that has more than one LUN, acts
as a SCSI disk and has firmware revision 123 you would add
something like:
{
T_DIRECT, T_FIXED, "MUMBLETECH", "BRIDGE2000", "123",
"sd", SC_MORE_LUS
}
The kernel on boot scans the inquiry data it receives against
the table and acts accordingly. See the source for more
info.
Tagged command queuing
Modern SCSI devices, particularly magnetic disks,
support what is called tagged command queuing (TCQ).
In a nutshell, TCQ allows the device to have multiple I/O
requests outstanding at the same time. Because the device is
intelligent, it can optimize its operations (like head
positioning) based on its own request queue. On SCSI devices
like RAID (Redundant Array of Independent Disks) arrays the TCQ
function is indispensable to take advantage of the device's
inherent parallelism.
Each I/O request is uniquely identified by a tag
(hence the name tagged command queuing) and this tag is used by
FreeBSD to see which I/O in the device drivers queue is reported
as complete by the device.
It should be noted however that TCQ requires device driver
support and that some devices implemented it not quite
right
in their firmware. This problem bit me once, and it
leads to highly mysterious problems. In such cases, try to
disable TCQ.
Busmaster host adapters
Most, but not all, SCSI host adapters are bus mastering
controllers. This means that they can do I/O on their own without
putting load onto the host CPU for data movement.
This is of course an advantage for a multitasking operating
system like FreeBSD. It must be noted however that there might be
some rough edges.
For instance an Adaptec 1542 controller can be set to use
different transfer speeds on the host bus (ISA or AT in this
case). The controller is settable to different rates because not
all motherboards can handle the higher speeds. Problems like
hang-ups, bad data etc might be the result of using a higher data
transfer rate then your motherboard can stomach.
The solution is of course obvious: switch to a lower data
transfer rate and try if that works better.
In the case of a Adaptec 1542, there is an option that can be
put into the kernel config file to allow dynamic determination of
the right, read: fastest feasible, transfer rate. This option is
disabled by default:
options "TUNE_1542" #dynamic tune of bus DMA speed
Check the man pages for the host adapter that you use. Or
better still, use the ultimate documentation (read: driver
source).
Tracking down problems
The following list is an attempt to give a guideline for the
most common SCSI problems and their solutions. It is by no means
complete.
Check for loose connectors and cables.
Check and double check the location and number of your
terminators.
Check if your bus has at least one supplier of terminator
power (especially with external terminators.
Check if no double target IDs are used.
Check if all devices to be used are powered up.
Make a minimal bus config with as little devices as
possible.
If possible, configure your host adapter to use slow bus
speeds.
Disable tagged command queuing to make things as simple as
possible (for a NCR host adapter based system see man
ncrcontrol)
If you can compile a kernel, make one with the
SCSIDEBUG option, and try accessing the
device with debugging turned on for that device. If your device
does not even probe at startup, you may have to define the
address of the device that is failing, and the desired debug
level in /sys/scsi/scsidebug.h . If it
probes but just does not work, you can use the
&man.scsi.8; command to dynamically set a debug level to
it in a running kernel (if SCSIDEBUG is
defined). This will give you copious
debugging output with which to confuse the gurus. See
man 4 scsi for more exact information. Also
look at man 8 scsi .
Further reading
If you intend to do some serious SCSI hacking, you might want to
have the official standard at hand:
Approved American National Standards can be purchased from
ANSI at
13th Floor
11 West 42nd Street
New York
NY 10036
Sales Dept: (212) 642-4900
You can also buy many ANSI
standards and most committee draft documents from Global
Engineering Documents,
15 Inverness Way East
Englewood
CO , 80112-5704
Phone: (800) 854-7179
Outside USA and Canada: (303) 792-2181
Fax: (303) 792- 2192
Many X3T10 draft documents are available electronically on the
SCSI BBS (719-574-0424) and on the ncrinfo.ncr.com anonymous ftp site.
Latest X3T10 committee documents are:
AT Attachment (ATA or IDE) [X3.221-1994]
(Approved )
ATA Extensions (ATA-2) [X3T10/948D Rev 2i]
Enhanced Small Device Interface (ESDI)
[X3.170-1990/X3.170a-1991]
(Approved )
Small Computer System Interface — 2 (SCSI-2)
[X3.131-1994] (Approved )
SCSI-2 Common Access Method Transport and SCSI Interface
Module (CAM) [X3T10/792D Rev 11]
Other publications that might provide you with additional
information are:
SCSI: Understanding the Small Computer System
Interface
, written by NCR Corporation. Available from:
Prentice Hall, Englewood Cliffs, NJ, 07632 Phone: (201) 767-5937
ISBN 0-13-796855-8
Basics of SCSI
, a SCSI tutorial written by
Ancot Corporation Contact Ancot for availability information at:
Phone: (415) 322-5322 Fax: (415) 322-0455
SCSI Interconnection Guide Book
, an AMP
publication (dated 4/93, Catalog 65237) that lists the various
SCSI connectors and suggests cabling schemes. Available from
AMP at (800) 522-6752 or (717) 564-0100
Fast Track to SCSI
, A Product Guide written by
Fujitsu. Available from: Prentice Hall, Englewood Cliffs, NJ,
07632 Phone: (201) 767-5937 ISBN 0-13-307000-X
The SCSI Bench Reference
, The SCSI
Encyclopedia
, and the SCSI Tutor
, ENDL
Publications, 14426 Black Walnut Court, Saratoga CA, 95070
Phone: (408) 867-6642
Zadian SCSI Navigator
(quick ref. book) and
Discover the Power of SCSI
(First book along with
a one-hour video and tutorial book), Zadian Software, Suite 214,
1210 S. Bascom Ave., San Jose, CA 92128, (408) 293-0800
On Usenet the newsgroups comp.periphs.scsi and comp.periphs are noteworthy places
to look for more info. You can also find the SCSI-Faq there, which
is posted periodically.
Most major SCSI device and host adapter suppliers operate ftp
sites and/or BBS systems. They may be valuable sources of
information about the devices you own.
* Disk/tape controllers
* SCSI
* IDE
* Floppy
Hard drives
SCSI hard drives
Contributed by &a.asami;. 17 February
1998.
As mentioned in the SCSI section,
virtually all SCSI hard drives sold today are SCSI-2 compliant and
thus will work fine as long as you connect them to a supported SCSI
host adapter. Most problems people encounter are either due to
badly designed cabling (cable too long, star topology, etc.),
insufficient termination, or defective parts. Please refer to the
SCSI section first if your SCSI hard
drive is not working. However, there are a couple of things you may
want to take into account before you purchase SCSI hard drives for
your system.
Rotational speed
Rotational speeds of SCSI drives sold today range from around
4,500RPM to 10,000RPM. Most of them are either 5,400RPM or
7,200RPM. Even though the 7,200RPM drives can generally transfer
data faster, they run considerably hotter than their 5,400RPM
counterparts. A large fraction of today's disk drive malfunctions
are heat-related. If you do not have very good cooling in your PC
case, you may want to stick with 5,400RPM or slower drives.
Note that newer drives, with higher areal recording densities,
can deliver much more bits per rotation than older ones. Today's
top-of-line 5,400RPM drives can sustain a throughput comparable to
7,200RPM drives of one or two model generations ago. The number
to find on the spec sheet for bandwidth is internal data
(or transfer) rate
. It is usually in megabits/sec so
divide it by 8 and you'll get the rough approximation of how much
megabytes/sec you can get out of the drive.
(If you are a speed maniac and want a 10,000RPM drive for your
cute little PC, be my guest; however, those drives become
extremely hot. Don't even think about it if you don't have a fan
blowing air directly at the drive or a
properly ventilated disk enclosure.)
Obviously, the latest 10,000RPM drives and 7,200RPM drives can
deliver more data than the latest 5,400RPM drives, so if absolute
bandwidth is the necessity for your applications, you have little
choice but to get the faster drives. Also, if you need low
latency, faster drives are better; not only do they usually have
lower average seek times, but also the rotational delay is one
place where slow-spinning drives can never beat a faster one.
(The average rotational latency is half the time it takes to
rotate the drive once; thus, it's 3 milliseconds for 10,000RPM
drives, 4.2ms for 7,200RPM drives and 5.6ms for 5,400RPM drives.)
Latency is seek time plus rotational delay. Make sure you
understand whether you need low latency or more accesses per
second, though; in the latter case (e.g., news servers), it may
not be optimal to purchase one big fast drive. You can achieve
similar or even better results by using the ccd (concatenated
disk) driver to create a striped disk array out of multiple slower
drives for comparable overall cost.
Make sure you have adequate air flow around the drive,
especially if you are going to use a fast-spinning drive. You
generally need at least 1/2" (1.25cm) of spacing above and below a
drive. Understand how the air flows through your PC case. Most
cases have the power supply suck the air out of the back. See
where the air flows in, and put the drive where it will have the
largest volume of cool air flowing around it. You may need to seal
some unwanted holes or add a new fan for effective cooling.
Another consideration is noise. Many 7,200 or faster drives
generate a high-pitched whine which is quite unpleasant to most
people. That, plus the extra fans often required for cooling, may
make 7,200 or faster drives unsuitable for some office and home
environments.
Form factor
Most SCSI drives sold today are of 3.5" form factor. They
come in two different heights; 1.6" (half-height
) or
1" (low-profile
). The half-height drive is the same
height as a CDROM drive. However, don't forget the spacing rule
mentioned in the previous section. If you have three standard
3.5" drive bays, you will not be able to put three half-height
drives in there (without frying them, that is).
Interface
The majority of SCSI hard drives sold today are Ultra or
Ultra-wide SCSI. The maximum bandwidth of Ultra SCSI is 20MB/sec,
and Ultra-wide SCSI is 40MB/sec. There is no difference in max
cable length between Ultra and Ultra-wide; however, the more
devices you have on the same bus, the sooner you will start having
bus integrity problems. Unless you have a well-designed disk
enclosure, it is not easy to make more than 5 or 6 Ultra SCSI
drives work on a single bus.
On the other hand, if you need to connect many drives, going
for Fast-wide SCSI may not be a bad idea. That will have the same
max bandwidth as Ultra (narrow) SCSI, while electronically it's
much easier to get it right
. My advice would be: if
you want to connect many disks, get wide SCSI drives; they usually
cost a little more but it may save you down the road. (Besides,
if you can't afford the cost difference, you shouldn't be building
a disk array.)
There are two variant of wide SCSI drives; 68-pin and 80-pin
SCA (Single Connector Attach). The SCA drives don't have a
separate 4-pin power connector, and also read the SCSI ID settings
through the 80-pin connector. If you are really serious about
building a large storage system, get SCA drives and a good SCA
enclosure (dual power supply with at least one extra fan). They
are more electronically sound than 68-pin counterparts because
there is no stub
of the SCSI bus inside the disk
canister as in arrays built from 68-pin drives. They are easier
to install too (you just need to screw the drive in the canister,
instead of trying to squeeze in your fingers in a tight place to
hook up all the little cables (like the SCSI ID and disk activity
LED lines).
* IDE hard drives
Tape drives
Contributed by &a.jmb;. 2 July
1996.
General tape access commands
&man.mt.1; provides generic access to the tape drives. Some of
the more common commands are rewind ,
erase , and status . See the
&man.mt.1; manual page for a detailed description.
Controller Interfaces
There are several different interfaces that support tape drives.
The interfaces are SCSI, IDE, Floppy and Parallel Port. A wide
variety of tape drives are available for these interfaces.
Controllers are discussed in Disk/tape
controllers.
SCSI drives
The &man.st.4; driver provides support for 8mm (Exabyte), 4mm
(DAT: Digital Audio Tape), QIC (Quarter-Inch Cartridge), DLT
(Digital Linear Tape), QIC Mini cartridge and 9-track (remember the
big reels that you see spinning in Hollywood computer rooms) tape
drives. See the &man.st.4; manual page for a detailed
description.
The drives listed below are currently being used by members of
the FreeBSD community. They are not the only drives that will work
with FreeBSD. They just happen to be the ones that we use.
4mm (DAT: Digital Audio Tape)
Archive Python
28454
Archive Python
04687
HP C1533A
HP C1534A
HP 35450A
HP 35470A
HP 35480A
SDT-5000
Wangtek
6200
8mm (Exabyte)
EXB-8200
EXB-8500
EXB-8505
QIC (Quarter-Inch Cartridge)
Archive Anaconda
2750
Archive Viper
60
Archive Viper
150
Archive Viper
2525
Tandberg TDC
3600
Tandberg TDC
3620
Tandberg TDC
3800
Tandberg TDC
4222
Wangtek
5525ES
DLT (Digital Linear Tape)
Digital TZ87
Mini-Cartridge
Conner CTMS
3200
Exabyte 2501
Autoloaders/Changers
Hewlett-Packard HP C1553A
Autoloading DDS2
* IDE drives
Floppy drives
Conner 420R
* Parallel port drives
Detailed Information
Archive Anaconda 2750
The boot message identifier for this drive is ARCHIVE
ANCDA 2750 28077 -003 type 1 removable SCSI 2
This is a QIC tape drive.
Native capacity is 1.35GB when using QIC-1350 tapes. This
drive will read and write QIC-150 (DC6150), QIC-250 (DC6250), and
QIC-525 (DC6525) tapes as well.
Data transfer rate is 350kB/s using
&man.dump.8;. Rates of 530kB/s have been reported when using
Amanda
Production of this drive has been discontinued.
The SCSI bus connector on this tape drive is reversed from
that on most other SCSI devices. Make sure that you have enough
SCSI cable to twist the cable one-half turn before and after the
Archive Anaconda tape drive, or turn your other SCSI devices
upside-down.
Two kernel code changes are required to use this drive. This
drive will not work as delivered.
If you have a SCSI-2 controller, short jumper 6. Otherwise,
the drive behaves are a SCSI-1 device. When operating as a SCSI-1
device, this drive, locks
the SCSI bus during some
tape operations, including: fsf, rewind, and rewoffl.
If you are using the NCR SCSI controllers, patch the file
/usr/src/sys/pci/ncr.c (as shown below).
Build and install a new kernel.
*** 4831,4835 ****
};
! if (np->latetime>4) {
/*
** Although we tried to wake it up,
--- 4831,4836 ----
};
! if (np->latetime>1200) {
/*
** Although we tried to wake it up,
Reported by: &a.jmb;
Archive Python 28454
The boot message identifier for this drive is ARCHIVE
Python 28454-XXX4ASB type 1 removable SCSI
2 density code 0x8c, 512-byte
blocks
This is a DDS-1 tape drive.
Native capacity is 2.5GB on 90m tapes.
Data transfer rate is XXX.
This drive was repackaged by Sun Microsystems as model
595-3067.
Reported by: Bob Bishop rb@gid.co.uk
Throughput is in the 1.5 MByte/sec range, however this will
drop if the disks and tape drive are on the same SCSI
controller.
Reported by: Robert E. Seastrom
rs@seastrom.com
Archive Python 04687
The boot message identifier for this drive is ARCHIVE
Python 04687-XXX 6580 Removable Sequential
Access SCSI-2 device
This is a DAT-DDS-2 drive.
Native capacity is 4GB when using 120m tapes.
This drive supports hardware data compression. Switch 4
controls MRS (Media Recognition System). MRS tapes have stripes
on the transparent leader. Switch 4 off
enables MRS, on disables MRS.
Parity is controlled by switch 5. Switch 5
on to enable parity control. Compression is
enabled with Switch 6 off . It is possible to
override compression with the SCSI MODE SELECT
command (see &man.mt.1;).
Data transfer rate is 800kB/s.
Archive Viper 60
The boot message identifier for this drive is ARCHIVE
VIPER 60 21116 -007 type 1 removable SCSI
1
This is a QIC tape drive.
Native capacity is 60MB.
Data transfer rate is XXX.
Production of this drive has been discontinued.
Reported by: Philippe Regnauld
regnauld@hsc.fr
Archive Viper 150
The boot message identifier for this drive is ARCHIVE
VIPER 150 21531 -004 Archive Viper 150 is a
known rogue type 1 removable SCSI
1 . A multitude of firmware revisions exist for this
drive. Your drive may report different numbers (e.g
21247 -005 .
This is a QIC tape drive.
Native capacity is 150/250MB. Both 150MB (DC6150) and 250MB
(DC6250) tapes have the recording format. The 250MB tapes are
approximately 67% longer than the 150MB tapes. This drive can
read 120MB tapes as well. It can not write 120MB tapes.
Data transfer rate is 100kB/s
This drive reads and writes DC6150 (150MB) and DC6250 (250MB)
tapes.
This drives quirks are known and pre-compiled into the scsi
tape device driver (&man.st.4;).
Under FreeBSD 2.2-CURRENT, use mt blocksize
512 to set the blocksize. (The particular drive had
firmware revision 21247 -005. Other firmware revisions may behave
differently) Previous versions of FreeBSD did not have this
problem.
Production of this drive has been discontinued.
Reported by: Pedro A M Vazquez
vazquez@IQM.Unicamp.BR
&a.msmith;
Archive Viper 2525
The boot message identifier for this drive is ARCHIVE
VIPER 2525 25462 -011 type 1 removable SCSI
1
This is a QIC tape drive.
Native capacity is 525MB.
Data transfer rate is 180kB/s at 90 inches/sec.
The drive reads QIC-525, QIC-150, QIC-120 and QIC-24 tapes.
Writes QIC-525, QIC-150, and QIC-120.
Firmware revisions prior to 25462 -011 are
bug ridden and will not function properly.
Production of this drive has been discontinued.
Conner 420R
The boot message identifier for this drive is Conner
tape .
This is a floppy controller, mini cartridge tape drive.
Native capacity is XXXX
Data transfer rate is XXX
The drive uses QIC-80 tape cartridges.
Reported by: Mark Hannon
mark@seeware.DIALix.oz.au
Conner CTMS 3200
The boot message identifier for this drive is CONNER
CTMS 3200 7.00 type 1 removable SCSI
2 .
This is a mini cartridge tape drive.
Native capacity is XXXX
Data transfer rate is XXX
The drive uses QIC-3080 tape cartridges.
Reported by: Thomas S. Traylor
tst@titan.cs.mci.com
DEC TZ87
The boot message identifier for this drive is DEC
TZ87 (C) DEC 9206 type 1 removable SCSI
2 density code 0x19
This is a DLT tape drive.
Native capacity is 10GB.
This drive supports hardware data compression.
Data transfer rate is 1.2MB/s.
This drive is identical to the Quantum DLT2000. The drive
firmware can be set to emulate several well-known drives,
including an Exabyte 8mm drive.
Reported by: &a.wilko;
Exabyte EXB-2501
The boot message identifier for this drive is EXABYTE
EXB-2501
This is a mini-cartridge tape drive.
Native capacity is 1GB when using MC3000XL
mini cartridges.
Data transfer rate is XXX
This drive can read and write DC2300 (550MB), DC2750 (750MB),
MC3000 (750MB), and MC3000XL (1GB) mini cartridges.
WARNING: This drive does not meet the SCSI-2 specifications.
The drive locks up completely in response to a SCSI MODE_SELECT
command unless there is a formatted tape in the drive. Before
using this drive, set the tape blocksize with
&prompt.root; mt -f /dev/st0ctl.0 blocksize 1024
Before using a mini cartridge for the first time, the
mini cartridge must be formated. FreeBSD 2.1.0-RELEASE and
earlier:
&prompt.root; /sbin/scsi -f /dev/rst0.ctl -s 600 -c "4 0 0 0 0 0"
(Alternatively, fetch a copy of the
scsiformat shell script from FreeBSD
2.1.5/2.2.) FreeBSD 2.1.5 and later:
&prompt.root; /sbin/scsiformat -q -w /dev/rst0.ctl
Right now, this drive cannot really be recommended for
FreeBSD.
Reported by: Bob Beaulieu
ez@eztravel.com
Exabyte EXB-8200
The boot message identifier for this drive is EXABYTE
EXB-8200 252X type 1 removable SCSI
1
This is an 8mm tape drive.
Native capacity is 2.3GB.
Data transfer rate is 270kB/s.
This drive is fairly slow in responding to the SCSI bus during
boot. A custom kernel may be required (set SCSI_DELAY to 10
seconds).
There are a large number of firmware configurations for this
drive, some have been customized to a particular vendor's
hardware. The firmware can be changed via EPROM
replacement.
Production of this drive has been discontinued.
Reported by: &a.msmith;
Exabyte EXB-8500
The boot message identifier for this drive is EXABYTE
EXB-8500-85Qanx0 0415 type 1 removable SCSI
2
This is an 8mm tape drive.
Native capacity is 5GB.
Data transfer rate is 300kB/s.
Reported by: Greg Lehey grog@lemis.de
Exabyte EXB-8505
The boot message identifier for this drive is
EXABYTE EXB-85058SQANXR1 05B0 type 1
removable SCSI 2
This is an 8mm tape drive which supports compression, and is
upward compatible with the EXB-5200 and EXB-8500.
Native capacity is 5GB.
The drive supports hardware data compression.
Data transfer rate is 300kB/s.
Reported by: Glen Foster
gfoster@gfoster.com
Hewlett-Packard HP C1533A
The boot message identifier for this drive is HP
C1533A 9503 type 1 removable SCSI
2 .
This is a DDS-2 tape drive. DDS-2 means hardware data
compression and narrower tracks for increased data
capacity.
Native capacity is 4GB when using 120m tapes. This drive
supports hardware data compression.
Data transfer rate is 510kB/s.
This drive is used in Hewlett-Packard's SureStore 6000eU and
6000i tape drives and C1533A DDS-2 DAT drive.
The drive has a block of 8 dip switches. The proper settings
for FreeBSD are: 1 ON; 2 ON; 3 OFF; 4 ON; 5 ON; 6 ON; 7 ON; 8
ON.
switch 1
switch 2
Result
On
On
Compression enabled at power-on, with host
control
On
Off
Compression enabled at power-on, no host
control
Off
On
Compression disabled at power-on, with host
control
Off
Off
Compression disabled at power-on, no host
control
Switch 3 controls MRS (Media Recognition System). MRS tapes
have stripes on the transparent leader. These identify the tape
as DDS (Digital Data Storage) grade media. Tapes that do not have
the stripes will be treated as write-protected. Switch 3 OFF
enables MRS. Switch 3 ON disables MRS.
See HP
SureStore Tape Products and Hewlett-Packard
Disk and Tape Technical Information for more information
on configuring this drive.
Warning: Quality control on these drives
varies greatly. One FreeBSD core-team member has returned 2 of
these drives. Neither lasted more than 5 months.
Reported by: &a.se;
Hewlett-Packard HP 1534A
The boot message identifier for this drive is HP
HP35470A T503 type 1 removable SCSI
2 Sequential-Access density code 0x13,
variable blocks .
This is a DDS-1 tape drive. DDS-1 is the original DAT tape
format.
Native capacity is 2GB when using 90m tapes.
Data transfer rate is 183kB/s.
The same mechanism is used in Hewlett-Packard's SureStore
2000i
tape drive, C35470A DDS format DAT drive, C1534A DDS format DAT
drive and HP C1536A DDS format DAT drive.
The HP C1534A DDS format DAT drive has two indicator lights,
one green and one amber. The green one indicates tape action:
slow flash during load, steady when loaded, fast flash during
read/write operations. The amber one indicates warnings: slow
flash when cleaning is required or tape is nearing the end of its
useful life, steady indicates an hard fault. (factory service
required?)
Reported by Gary Crutcher
gcrutchr@nightflight.com
Hewlett-Packard HP C1553A Autoloading DDS2
The boot message identifier for this drive is "".
This is a DDS-2 tape drive with a tape changer. DDS-2 means
hardware data compression and narrower tracks for increased data
capacity.
Native capacity is 24GB when using 120m tapes. This drive
supports hardware data compression.
Data transfer rate is 510kB/s (native).
This drive is used in Hewlett-Packard's SureStore 12000e
tape drive.
The drive has two selectors on the rear panel. The selector
closer to the fan is SCSI id. The other selector should be set to
7.
There are four internal switches. These should be set: 1 ON;
2 ON; 3 ON; 4 OFF.
At present the kernel drivers do not automatically change
tapes at the end of a volume. This shell script can be used to
change tapes:
#!/bin/sh
PATH="/sbin:/usr/sbin:/bin:/usr/bin"; export PATH
usage()
{
echo "Usage: dds_changer [123456ne] raw-device-name
echo "1..6 = Select cartridge"
echo "next cartridge"
echo "eject magazine"
exit 2
}
if [ $# -ne 2 ] ; then
usage
fi
cdb3=0
cdb4=0
cdb5=0
case $1 in
[123456])
cdb3=$1
cdb4=1
;;
n)
;;
e)
cdb5=0x80
;;
?)
usage
;;
esac
scsi -f $2 -s 100 -c "1b 0 0 $cdb3 $cdb4 $cdb5"
Hewlett-Packard HP 35450A
The boot message identifier for this drive is HP
HP35450A -A C620 type 1 removable SCSI
2 Sequential-Access density code
0x13
This is a DDS-1 tape drive. DDS-1 is the original DAT tape
format.
Native capacity is 1.2GB.
Data transfer rate is 160kB/s.
Reported by: Mark Thompson
mark.a.thompson@pobox.com
Hewlett-Packard HP 35470A
The boot message identifier for this drive is HP
HP35470A 9 09 type 1 removable SCSI
2
This is a DDS-1 tape drive. DDS-1 is the original DAT tape
format.
Native capacity is 2GB when using 90m tapes.
Data transfer rate is 183kB/s.
The same mechanism is used in Hewlett-Packard's SureStore
2000i
tape drive, C35470A DDS format DAT drive, C1534A DDS format DAT
drive, and HP C1536A DDS format DAT drive.
Warning: Quality control on these drives
varies greatly. One FreeBSD core-team member has returned 5 of
these drives. None lasted more than 9 months.
Reported by: David Dawes
dawes@rf900.physics.usyd.edu.au (9 09)
Hewlett-Packard HP 35480A
The boot message identifier for this drive is HP
HP35480A 1009 type 1 removable SCSI
2 Sequential-Access density code
0x13 .
This is a DDS-DC tape drive. DDS-DC is DDS-1 with hardware
data compression. DDS-1 is the original DAT tape format.
Native capacity is 2GB when using 90m tapes. It cannot handle
120m tapes. This drive supports hardware data compression.
Please refer to the section on HP C1533A for the proper
switch settings.
Data transfer rate is 183kB/s.
This drive is used in Hewlett-Packard's SureStore 5000eU and
5000i
tape drives and C35480A DDS format DAT drive..
This drive will occasionally hang during a tape eject
operation (mt offline ). Pressing the front
panel button will eject the tape and bring the tape drive back to
life.
WARNING: HP 35480-03110 only. On at least two occasions this
tape drive when used with FreeBSD 2.1.0, an IBM Server 320 and an
2940W SCSI controller resulted in all SCSI disk partitions being
lost. The problem has not be analyzed or resolved at this
time.
Sony SDT-5000
There are at least two significantly different models: one is
a DDS-1 and the other DDS-2. The DDS-1 version is
SDT-5000 3.02 . The DDS-2 version is
SONY SDT-5000 327M . The DDS-2 version has a 1MB
cache. This cache is able to keep the tape streaming in almost
any circumstances.
The boot message identifier for this drive is SONY
SDT-5000 3.02 type 1 removable SCSI
2 Sequential-Access density code
0x13
Native capacity is 4GB when using 120m tapes. This drive
supports hardware data compression.
Data transfer rate is depends upon the model or the drive. The
rate is 630kB/s for the SONY SDT-5000 327M
while compressing the data. For the SONY SDT-5000
3.02 , the data transfer rate is 225kB/s.
In order to get this drive to stream, set the blocksize to 512
bytes (mt blocksize 512 ) reported by Kenneth
Merry ken@ulc199.residence.gatech.edu .
SONY SDT-5000 327M information reported by
Charles Henrich henrich@msu.edu .
Reported by: &a.jmz;
Tandberg TDC 3600
The boot message identifier for this drive is
TANDBERG TDC 3600 =08: type 1
removable SCSI 2
This is a QIC tape drive.
Native capacity is 150/250MB.
This drive has quirks which are known and work around code is
present in the scsi tape device driver (&man.st.4;).
Upgrading the firmware to XXX version will fix the quirks and
provide SCSI 2 capabilities.
Data transfer rate is 80kB/s.
IBM and Emerald units will not work. Replacing the firmware
EPROM of these units will solve the problem.
Reported by: &a.msmith;
Tandberg TDC 3620
This is very similar to the Tandberg TDC 3600
drive.
Reported by: &a.joerg;
Tandberg TDC 3800
The boot message identifier for this drive is
TANDBERG TDC 3800 =04Y Removable
Sequential Access SCSI-2 device
This is a QIC tape drive.
Native capacity is 525MB.
Reported by: &a.jhs;
Tandberg TDC 4222
The boot message identifier for this drive is
TANDBERG TDC 4222 =07 type 1 removable
SCSI 2
This is a QIC tape drive.
Native capacity is 2.5GB. The drive will read all cartridges
from the 60 MB (DC600A) upwards, and write 150 MB (DC6150)
upwards. Hardware compression is optionally supported for the 2.5
GB cartridges.
This drives quirks are known and pre-compiled into the scsi
tape device driver (&man.st.4;) beginning with FreeBSD
2.2-CURRENT. For previous versions of FreeBSD, use
mt to read one block from the tape, rewind the
tape, and then execute the backup program (mt fsr 1; mt
rewind; dump ... )
Data transfer rate is 600kB/s (vendor claim with compression),
350 KB/s can even be reached in start/stop mode. The rate
decreases for smaller cartridges.
Reported by: &a.joerg;
Wangtek 5525ES
The boot message identifier for this drive is WANGTEK
5525ES SCSI REV7 3R1 type 1 removable SCSI
1 density code 0x11, 1024-byte
blocks
This is a QIC tape drive.
Native capacity is 525MB.
Data transfer rate is 180kB/s.
The drive reads 60, 120, 150, and 525MB tapes. The drive will
not write 60MB (DC600 cartridge) tapes. In order to overwrite 120
and 150 tapes reliably, first erase (mt erase )
the tape. 120 and 150 tapes used a wider track (fewer tracks per
tape) than 525MB tapes. The extra
width of the
previous tracks is not overwritten, as a result the new data lies
in a band surrounded on both sides by the previous data unless the
tape have been erased.
This drives quirks are known and pre-compiled into the scsi
tape device driver (&man.st.4;).
Other firmware revisions that are known to work are:
M75D
Reported by: Marc van Kempen marc@bowtie.nl
REV73R1 Andrew Gordon
Andrew.Gordon@net-tel.co.uk
M75D
Wangtek 6200
The boot message identifier for this drive is WANGTEK
6200-HS 4B18 type 1 removable SCSI
2 Sequential-Access density code
0x13
This is a DDS-1 tape drive.
Native capacity is 2GB using 90m tapes.
Data transfer rate is 150kB/s.
Reported by: Tony Kimball alk@Think.COM
* Problem drives
CDROM drives
Contributed by &a.obrien;. 23 November
1997.
As mentioned in Jordan's
Picks Generally speaking those in The FreeBSD
Project prefer SCSI CDROM drives over IDE CDROM drives.
However not all SCSI CDROM drives are equal. Some feel the quality of
some SCSI CDROM drives have been deteriorating to that of IDE CDROM
drives. Toshiba used to be the favored stand-by, but many on the SCSI
mailing list have found displeasure with the 12x speed XM-5701TA as
its volume (when playing audio CDROMs) is not controllable by the
various audio player software.
Another area where SCSI CDROM manufacturers are cutting corners is
adherence to the SCSI
specification. Many SCSI CDROMs will respond to multiple LUNs for its target
address. Known violators include the 6x Teac CD-56S 1.0D.
* Other
* Other
* PCMCIA
diff --git a/en_US.ISO8859-1/books/handbook/ports/chapter.sgml b/en_US.ISO8859-1/books/handbook/ports/chapter.sgml
index a5d0479b5a..063fc1ecc6 100644
--- a/en_US.ISO8859-1/books/handbook/ports/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/ports/chapter.sgml
@@ -1,1254 +1,1254 @@
Installing Applications: Packages and Ports
Synopsis
There is only so much you can do with FreeBSD. If you are an
operating systems developer then the base system likely contains
everything you need. If that is not what you are planning to do with
FreeBSD then you will probably want to install additional
software—perhaps a web server, or a mail reader, or a graphical
environment such as KDE or GNOME.
If you have used a Unix system before you will know that the typical
procedure for installing third party software goes something like
this:
Download the software, which might be distributed in source code
format, or as a binary.
Unpack the software from its distribution format (typically a
tarball compressed with either &man.compress.1; or &man.gzip.1;).
Locate the documentation (perhaps a README
file, or some files in a doc/ subdirectory) and
read up on how to install the software.
If the software was distributed in source format, compile it.
This may involve editing a Makefile , or
running a configure script, and other work.
Test and install the software.
And that is only if everything goes well. If you are installing a
software package that was not deliberately ported to FreeBSD you may
even have to go in and edit the code to make it work properly.
Should you want to, you can continue to install software the
traditional
way with FreeBSD. However, FreeBSD provides
two technologies which can save you a lot of effort; packages and
ports. At the time of writing, over 4,000 third party applications have
been made available in this way.
For any given application, the FreeBSD package for that application
is a single file which you must download. The package contains
pre-compiled copies of all the commands for the application, as well as
any configuration files or documentation. A downloaded package file can
be manipulated with FreeBSD pkg_* commands, such as
&man.pkg.add.1; &man.pkg.delete.1;, &man.pkg.info.1;, and so on.
Installing a new application can be carried out with a single
command.
A FreeBSD port for an application is a collection of files designed
to automate the process of compiling an application from source
code.
Remember that there are a number of steps you would normally carry
out if you compiled a program yourself (unpacking, patching, compiling,
installing). The files that make up a port contain all the necessary
information to alllow the system to do this for you. You run a handful
of simple commands and the source code for the application is
automatically downloaded, extracted, patched, compiled, and installed
for you.
In fact, the ports system can also be used to generate packages
which can later be manipulated with the pkg_*
commands.
Both packages and ports understand
dependencies . Suppose you want to install an
application that depends on a specific library being installed. Both
the application and the library have been made available as FreeBSD
ports and packages. If you use the pkg_add command
or the ports system to add the application, both will notice that the
library has not been installed, and the commands will install the
library first.
Given that the two technologies are quite similar, you might be
wondering why FreeBSD bothers with both. Packages and ports both have
their own strengths, and which one you use will depend on your own
preference.
Package benefits
A compressed package tarball is typically smaller than the
compressed tarball containing the source code for the application.
Packages do not require any additional compilation. For large
applications, such as Mozilla ,
KDE , or GNOME
this can be important, particularly if you are on a slow system.
Packages do not require you to understand any of the process
involved in compiling software on FreeBSD.
Ports benefits
Packages are normally compiled with conservative options,
because they have to run on the maximum number of systems. By
installing from the port, you can tweak the compilation options to
(for example) generate code that is specific to a 686 processor.
Some packages have compile time options relating to what they
can and can't do. For example, Apache
can be configured with a wide variety of different builtin options.
By building from the port you do not have to accept the default
options, and can set them yourself.
In some cases, multiple packages will exist for the same
application to specify certain settings. For example,
Ghostscript is available as a
ghostscript package and a
ghostscript-nox11 package, depending on whether
or not you have installed an X11 server. This sort of rough
tweaking is possible with packages, but rapidly becomes impossible
if an application has more than one or two different compile time
options.
The licensing conditions of some software distributions forbid
binary distribution. They must be distributed as source
code.
Some people do not trust binary distributions. At least with
source code, you can (in theory) read through it and look for
potential problems yourself.
If you have local patches, you will need the source in order to
apply them.
Some people like having code around, so they can read it if they
get bored, hack it, borrow from it (license permitting, of course),
and so on.
To keep track of updated ports, subscribe to
freebsd-ports .
The remainder of this chapter will explain how to use packages and
ports to install and manage third party software on FreeBSD.
Finding your application
Before you can install any applications you need to know what you
want, and what the application is called.
FreeBSD's list of available applications is growing all the time.
Currently there are over 4,000 applications available as packages or
ports. There are a number of ways to find what you want.
The FreeBSD web site maintains an up-to-date searchable list of
all the available applications, at
http://www.FreeBSD.org/ports/ .
The name space is divided in to categories, and you may either
search for an application by name (if you know it), or you can list
all the applications available in a category.
Dan Langille maintains FreshPorts, at
http://www.freshports.org/ .
FreshPorts tracks changes to the applications in the ports tree as
they happen, and allows you to watch
one or more
ports, and will send you an e-mail when they are updated.
If you do not know the name of the application you want, try
using a site like FreshMeat
(http://www.freshmeat.net/ )
or AppWatch
(http://www.appwatch.com/ )
to find an application, then check back at the FreeBSD site to see
if the application has been ported yet.
Using the Packages System
Contributed by &a.chern;, April 30, 2001.
Installing a Package
You can use the &man.pkg.add.1; utility to install a
FreeBSD software package from a local file or from a server on
the network.
Downloading a package and then installing it locally
&prompt.root; ftp ftp2.freebsd.org
Connected to ftp2.freebsd.org.
220 ftp2.freebsd.org FTP server (Version 6.00LS) ready.
331 Guest login ok, send your email address as password.
230-
230- This machine is in Vienna, VA, USA, hosted by Verio.
230- Questions? E-mail freebsd@vienna.verio.net.
230-
230-
230 Guest login ok, access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd /pub/FreeBSD/ports/packages/irc
250 CWD command successful.
ftp> get xchat-1.7.1.tgz
local: xchat-1.7.1.tgz remote: xchat-1.7.1.tgz
150 Opening BINARY mode data connection for 'xchat-1.7.1.tgz' (471488 bytes).
100% |**************************************************| 460 KB 00:00 ETA
226 Transfer complete.
471488 bytes received in 5.37 seconds (85.70 KB/s)
ftp> exit
&prompt.root; pkg_add xchat-1.7.1.tgz
&prompt.root;
If you don't have a source of local packages (such as a
FreeBSD CD-ROM set) then it will probably be easier to use the
-r option to &man.pkg.add.1;. This will cause the utility to
automatically determine the correct object format and release
and then to fetch and install the package from an FTP site.
&prompt.root; pkg_add -r xchat-1.7.1
This would download the correct package and add it without
any further user intervention.
Package files are distributed in .tgz format. You can
find them at ftp://ftp.freebsd.org/ports/packages ,
or on the FreeBSD CD-ROM distribution. Every CD on the
FreeBSD 4-CD set (and PowerPak, etc) contains packages in
the /packages directory. The layout of
the packages is similar to that of the
/usr/ports tree. Each category has its
own directory, and every package can be found within the
All directory.
The directory structure of the package system is homologous
to that of the ports; they work with each other to form the entire
package/port system.
Deleting a Package
&prompt.root pkg_delete xchat-1.7.1
&prompt.root
&man.pkg.delete.1; is the utility for removing
previously installed software package distributions.
Managing packages
&man.pkg.info.1; a utility that lists and describes
the various packages installed.
&prompt.root pkg_info
cvsup-bin-16.1 A general network file distribution system optimized for CV
docbook-1.2 Meta-port for the different versions of the DocBook DTD
...
&man.pkg.version.1; a utility that summarizes the
versions of all installed packages. It compares the package
version to the current version found in the ports tree.
&prompt.root pkg_version
cvsup-bin =
docbook =
...
The symbols in the second column indicate the relative age
of the installed version and the version available in the local
ports tree.
= The version of the
installed package matches that of the one found in the
local ports tree.
<
The installed version is older then the one available
in the ports tree.
> The installed version is newer
than the one found in the local ports tree. (local ports
tree is probably out of date)
? The installed package cannot be
found in the ports index.
* There are multiple versions of the
package.
Miscellaneous
&man.pkg.add.1; &man.pkg.delete.1; &man.pkg.info.1;
&man.pkg.version.1; &man.pkg.create.1;
All package information is stored within the
/var/db/pkg directory. The listing
of contents and descriptions of each package can be found within
files in this directory.
Using the Ports Collection
The following sections provide basic instructions on using the
ports collection to install or remove programs from your
system.
Installing Ports
The first thing that should be explained
when it comes to the Ports collection is what is actually meant
by a skeleton
. In a nutshell, a port skeleton is a
minimal set of files that are needed for a program to compile and
install cleanly on FreeBSD. Each port skeleton includes:
A Makefile . The
Makefile contains various statements that
specify how the application should be compiled and where it
should be installed on your system
A distinfo file. This file contains
information about the files that must be downloaded to build the
- port, and checksums, to ensure that that files have not been
+ port, and checksums, to ensure that those files have not been
corrupted during the download.
A files directory. This directory
contains patches to make the program compile and install on
your FreeBSD system. Patches are basically small files that
specify changes to particular files. They are in plain text
format, and basically say Remove line 10
or
Change line 26 to this ...
. Patches are also
known as diffs
because they are generated by the
diff program.
This directory may also contain other files used in building
the port.
A pkg-comment file. This is a one-line
description of the program.
A pkg-descr file. This is a more
detailed, often multiple-line, description of the program.
A pkg-plist file. This is a list of all
the files that will be installed by the port. It also tells the
ports system what files to remove upon deinstallation.
Now that you have enough background information to know what
the Ports collection is used for, you are ready to install your
first port. There are two ways this can be done, and each is
explained below.
Before we get into that however, you will need to choose a
port to install. There are a few ways to do this, with the
easiest method being the ports listing on the FreeBSD
web site . You can browse through the ports listed there
or use the search function on the site. Each port also includes
a description so you can read a bit about each port before
deciding to install it.
Another method is to use the whereis
command. To use whereis , simply type
whereis <program you want to
install>
at the prompt, and if it is found on
your system, you will be told where it is, like so:
&prompt.root; whereis xchat
xchat: /usr/ports/irc/xchat
&prompt.root;
This tells us that xchat (an irc client) can be found in the
/usr/ports/irc/xchat directory.
Yet another way of finding a particular port is by using the
Ports collection's built-in search mechanism. To use the search
feature, you will need to be in the
/usr/ports directory. Once in that
directory, run make search key=program-name
where program-name
is the name of the program you
want to find. For example, if you were looking for xchat:
&prompt.root; cd /usr/ports
&prompt.root; make search key=xchat
Port: xchat-1.3.8
Path: /usr/ports/irc/xchat
Info: An X11 IRC client using the GTK+ toolkit, and optionally, GNOME
Maint: jim@FreeBSD.org
Index: irc
B-deps: XFree86-3.3.5 bzip2-0.9.5d gettext-0.10.35 giflib-4.1.0 glib-1.2.6 gmake-3.77 gtk-1.2.6
imlib-1.9.8 jpeg-6b png-1.0.3 tiff-3.5.1
R-deps: XFree86-3.3.5 gettext-0.10.35 giflib-4.1.0 glib-1.2.6 gtk-1.2.6 imlib-1.9.8 jpeg-6b
png-1.0.3 tiff-3.5.1
The part of the output you want to pay particular attention
to is the Path:
line, since that tells you where to
find it. The other information provided is not needed in order
to install the port directly, so it will not be covered
here.
You must be the root user to install
ports.
Now that you have found a port you would like to install, you
are ready to do the actual installation.
Installing ports from a CDROM
As you may have guessed from the title, everything
described in this section assumes you have a FreeBSD CDROM set.
If you do not, you can order one from the FreeBSD Mall .
Assuming that your FreeBSD CDROM is in the drive and is
mounted on /cdrom (and the mount point
must be /cdrom ),
you are ready to install the port. To begin, change directories
to the directory where the port you want to install lives:
&prompt.root; cd /usr/ports/irc/xchat
Once inside the xchat directory, you will see the port
skeleton. The next step is to compile (also called build) the
port. This is done by simply typing make at
the prompt. Once you have done so, you should see something
like this:
&prompt.root; make
>> xchat-1.3.8.tar.bz2 doesn't seem to exist on this system.
>> Attempting to fetch from file:/cdrom/ports/distfiles/.
===> Extracting for xchat-1.3.8
>> Checksum OK for xchat-1.3.8.tar.bz2.
===> xchat-1.3.8 depends on executable: bzip2 - found
===> xchat-1.3.8 depends on executable: gmake - found
===> xchat-1.3.8 depends on shared library: gtk12.2 - found
===> xchat-1.3.8 depends on shared library: Imlib.5 - found
===> xchat-1.3.8 depends on shared library: X11.6 - found
===> Patching for xchat-1.3.8
===> Applying FreeBSD patches for xchat-1.3.8
===> Configuring for xchat-1.3.8
...
[configure output snipped]
...
===> Building for xchat-1.3.8
...
[compilation snipped]
...
&prompt.root;
Take notice that once the compile is complete you are
returned to your prompt. The next step is to install the
port. In order to install it, you simply need to tack one word
onto the make command, and that word is
install :
&prompt.root; make install
===> Installing for xchat-1.3.8
===> xchat-1.3.8 depends on shared library: gtk12.2 - found
===> xchat-1.3.8 depends on shared library: Imlib.5 - found
===> xchat-1.3.8 depends on shared library: X11.6 - found
...
[install routines snipped]
...
===> Generating temporary packing list
===> Installing xchat docs in /usr/X11R6/share/doc/xchat
===> Registering installation for xchat-1.3.8
&prompt.root;
Once you are returned to your prompt, you should be able to
run the application you just installed.
You can save an extra step by just running make
install instead of make and
make install as two separate steps.
Please be aware that the licenses of a few ports do not
allow for inclusion on the CDROM. This could be for various
- reasons, including things such as as registration form needs
+ reasons, including things such as registration form needs
to be filled out before downloading, if redistribution is not
allowed, and so on. If you wish to install a port not
included on the CDROM, you will need to be online in order to
do so (see the next
section).
Installing ports from the Internet
As with the last section, this section makes an assumption
that you have a working Internet connection. If you do not,
you will need to do the CDROM
installation.
Installing a port from the Internet is done exactly the same
way as it would be if you were installing from a CDROM. The
only difference between the two is that the program's source
code is downloaded from the Internet instead of pulled from the
CDROM.
The steps involved are identical:
&prompt.root; make install
>> xchat-1.3.8.tar.bz2 doesn't seem to exist on this system.
>> Attempting to fetch from http://xchat.org/files/v1.3/.
Receiving xchat-1.3.8.tar.bz2 (305543 bytes): 100%
305543 bytes transferred in 2.9 seconds (102.81 Kbytes/s)
===> Extracting for xchat-1.3.8
>> Checksum OK for xchat-1.3.8.tar.bz2.
===> xchat-1.3.8 depends on executable: bzip2 - found
===> xchat-1.3.8 depends on executable: gmake - found
===> xchat-1.3.8 depends on shared library: gtk12.2 - found
===> xchat-1.3.8 depends on shared library: Imlib.5 - found
===> xchat-1.3.8 depends on shared library: X11.6 - found
===> Patching for xchat-1.3.8
===> Applying FreeBSD patches for xchat-1.3.8
===> Configuring for xchat-1.3.8
...
[configure output snipped]
...
===> Building for xchat-1.3.8
...
[compilation snipped]
...
===> Installing for xchat-1.3.8
===> xchat-1.3.8 depends on shared library: gtk12.2 - found
===> xchat-1.3.8 depends on shared library: Imlib.5 - found
===> xchat-1.3.8 depends on shared library: X11.6 - found
...
[install routines snipped]
...
===> Generating temporary packing list
===> Installing xchat docs in /usr/X11R6/share/doc/xchat
===> Registering installation for xchat-1.3.8
&prompt.root;
As you can see, the only difference is the line that tells
you where the system is fetching the port from.
That about does it for installing ports onto your system.
In the section you will learn how to remove a port from your
system.
Removing Installed Ports
Now that you know how to install ports, you are probably
wondering how to remove them, just in case you install one and
later on you decide that you installed the wrong port. The next
few paragraphs will cover just that.
Now we will remove our previous example (which was xchat for
those of you not paying attention). As with installing ports,
the first thing you must do is change to the port directory,
which if you remember was
/usr/ports/irc/xchat . After you change
directories, you are ready to uninstall xchat. This is done with
the make deinstall command (makes sense
right?):
&prompt.root; cd /usr/ports/irc/xchat
&prompt.root; make deinstall
===> Deinstalling for xchat-1.3.8
&prompt.root;
That was easy enough. You have now managed to remove xchat
from your system. If you would like to reinstall it, you can do
so by running make reinstall from the
/usr/ports/irc/xchat directory.
Troubleshooting
The following sections cover some of the more frequently asked
questions about the Ports collection and some basic troubleshooting
techniques, and what do to if a port is broken.
Some Questions and Answers
I thought this was going to be a discussion about
modems??!
Ah, you must be thinking of the serial ports on the back
of your computer. We are using port
here to
mean the result of porting
a program from one
version of UNIX to another.
What is a patch?
A patch is a small file that specifies how to go from
one version of a file to another. It contains plain text,
and basically says things like delete line 23
,
add these two lines after line 468
, or
change line 197 to this
. They are also known
as diffs because they are generated by the
diff program.
What is all this about
tarballs?
It is a file ending in .tar , or
with variations such as .tar.gz ,
.tar.Z , .tar.bz2 ,
and even .tgz .
Basically, it is a directory tree that has been archived
into a single file (.tar ) and
optionally compressed (.gz ). This
technique was originally used for T ape
AR chives (hence the name
tar ), but it is a widely used way of
distributing program source code around the Internet.
You can see what files are in them, or even extract them
yourself by using the standard UNIX tar program, which comes
with the base FreeBSD system, like this:
&prompt.user; tar tvzf foobar.tar.gz
&prompt.user; tar xzvf foobar.tar.gz
&prompt.user; tar tvf foobar.tar
&prompt.user; tar xvf foobar.tar
And a checksum?
It is a number generated by adding up all the data in
the file you want to check. If any of the characters
change, the checksum will no longer be equal to the total,
so a simple comparison will allow you to spot the
difference.
I did what you said for compiling ports from a CDROM and
it worked great until I tried to install the kermit
port.
&prompt.root; make install
>> cku190.tar.gz doesn't seem to exist on this system.
>> Attempting to fetch from ftp://kermit.columbia.edu/kermit/archives/.
Why can it not be found? Have I got a dud CDROM?
As was explained in the compiling ports from CDROM
section, some ports cannot be put on the CDROM set
due to licensing restrictions. Kermit is an example of
that. The licensing terms for kermit do not allow us to put
the tarball for it on the CDROM, so you will have to fetch
it by hand—sorry!
The reason why you got all those error messages was
because you were not connected to the Internet at the time.
Once you have downloaded it from any of the MASTER_SITES
(listed in the Makefile), you can restart the install
process.
I did that, but when I tried to put it into
/usr/ports/distfiles I got some error
about not having permission.
The ports mechanism looks for the tarball in
/usr/ports/distfiles , but you will not
be able to copy anything there because it is symlinked to
the CDROM, which is read-only. You can tell it to look
somewhere else by doing:
&prompt.root; make DISTDIR=/where/you/put/it install
Does the ports scheme only work if you have everything
in /usr/ports ? My system administrator
says I must put everything under
/u/people/guests/wurzburger , but it
does not seem to work.
You can use the PORTSDIR and
PREFIX variables to tell the ports
mechanism to use different directories. For
instance,
&prompt.root; make PORTSDIR=/u/people/guests/wurzburger/ports install
will compile the port in
/u/people/guests/wurzburger/ports and
install everything under
/usr/local .
&prompt.root; make PREFIX=/u/people/guests/wurzburger/local install
will compile it in /usr/ports and
install it in
/u/people/guests/wurzburger/local .
And of course,
&prompt.root; make PORTSDIR=../ports PREFIX=../local install
will combine the two (it is too long to write fully on
the page, but it should give you the general idea).
Some ports that use &man.imake.1; (a part of the X Windows
System) don't work well with PREFIX , and will insist on
installing under /usr/X11R6 . Similarly, some Perl ports
ignore PREFIX and install in the Perl tree. Making these
ports respect PREFIX is a difficult or impossible
job.
If you do not fancy typing all that in every time you
install a port, it is a good idea to put these variables
into your environment. Read the man page for your shell for
instructions on doing so.
I do not have a FreeBSD CDROM, but I would like to have
all the tarballs handy on my system so I do not have to wait
for a download every time I install a port. Is there any
way to get them all at once?
To get every single tarball for the Ports collection,
do:
&prompt.root; cd /usr/ports
&prompt.root; make fetch
For all the tarballs for a single ports directory,
do:
&prompt.root; cd /usr/ports/directory
&prompt.root; make fetch
and for just one port—well, you have probably
guessed already.
I know it is probably faster to fetch the tarballs from
one of the FreeBSD mirror sites close by. Is there any way
to tell the port to fetch them from servers other than the
ones listed in the MASTER_SITES?
Yes. If you know, for example, that ftp.FreeBSD.org is much closer to you
than the sites listed in MASTER_SITES ,
do as follows:
&prompt.root; cd /usr/ports/directory
&prompt.root; make MASTER_SITE_OVERRIDE= \
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/distfiles/ fetch
I want to know what files make is
going to need before it tries to pull them down.
make fetch-list will display a list
of the files needed for a port.
Is there any way to stop the port from compiling? I
want to do some hacking on the source before I install it,
but it is a bit tiresome to watch it and hit control-C every
time.
Doing make extract will stop it
after it has fetched and extracted the source code.
I am trying to make my own port and I want to be able
to stop it compiling until I have had a chance to see if my
patches worked properly. Is there something like
make extract , but for patches?
Yep, make patch is what you want.
You will probably find the PATCH_DEBUG
option useful as well. And by the way, thank you for your
efforts!
I have heard that some compiler options can cause bugs.
Is this true? How can I make sure that I compile ports
with the right settings?
Yes, with version 2.6.3 of gcc (the
version shipped with FreeBSD 2.1.0 and 2.1.5), the
-O2 option could result in buggy code
unless you used the -fno-strength-reduce
option as well. (Most of the ports do not use
-O2 ). You should be
able to specify the compiler options used by something
like:
&prompt.root; make CFLAGS='-O2 -fno-strength-reduce' install
or by editing /etc/make.conf , but
unfortunately not all ports respect this. The surest way
is to do make configure , then go into
the source directory and inspect the Makefiles by hand, but
this can get tedious if the source has lots of
sub-directories, each with their own Makefiles.
The default FreeBSD compiler options are quite conservative,
so if you have not changed them you should not have any
problems.
There are so many ports it is hard to find the one I
want. Is there a list anywhere of what ports are
available?
Look in the INDEX file in
/usr/ports . If you would like to
search the ports collection for a keyword, you can do that
too. For example, you can find ports relevant to the LISP
programming language using:
&prompt.user; cd /usr/ports
&prompt.user; make search key=lisp
I went to install the foo port but
the system suddenly stopped compiling it and starting
compiling the bar port. What is going
on?
The foo port needs something that is
supplied with bar — for instance,
if foo uses graphics,
bar might have a library with useful
graphics processing routines. Or bar
might be a tool that is needed to compile the
foo port.
I installed the
grizzle program from the ports and
frankly it is a complete waste of disk space. I want to
delete it but I do not know where it put all the files.
Any clues?
No problem, just do:
&prompt.root; pkg_delete grizzle-6.5
Alternatively, you can do:
&prompt.root; cd /usr/ports/somewhere/grizzle
&prompt.root; make deinstall
Hang on a minute, you have to know the version number
to use that command. You do not seriously expect me to
remember that, do you??
Not at all, you can find it out by doing:
&prompt.root; pkg_info -a | grep grizzle
Information for grizzle-6.5:
grizzle-6.5 - the combined piano tutorial, LOGO interpreter and shoot 'em up
arcade game.
Talking of disk space, the ports directory seems to be
taking up an awful lot of room. Is it safe to go in there
and delete things?
Yes, if you have installed the program and are fairly
certain you will not need the source again, there is no
point in keeping it hanging around. The best way to do
this is:
&prompt.root; cd /usr/ports
&prompt.root; make clean
which will go through all the ports subdirectories and
delete everything except the skeletons for each
port.
I tried that and it still left all those tarballs or
whatever you called them in the
distfiles directory. Can I delete
those as well?
Yes, if you are sure you have finished with them,
those can go as well. They can be removed manually, or by
using make distclean .
I like having lots and lots of programs to play with.
Is there any way of installing all the ports in one
go?
Just do:
&prompt.root; cd /usr/ports
&prompt.root; make install
Be careful, as some ports may install files with the same
name. If you install two graphics ports and they both install
/usr/local/bin/plot then you will obviously
have problems.
OK, I tried that, but I thought it would take a very
long time so I went to bed and left it to get on with it.
When I looked at the computer this morning, it had only
done three and a half ports. Did something go
wrong?
No, the problem is that some of the ports need to ask
you questions that we cannot answer for you (e.g., Do
you want to print on A4 or US letter sized paper?
)
and they need to have someone on hand to answer
them.
I really do not want to spend all day staring at the
monitor. Any better ideas?
OK, do this before you go to bed/work/the local
park:
&prompt.root cd /usr/ports
&prompt.root; make -DBATCH install
This will install every port that does
not require user input. Then, when
you come back, do:
&prompt.root; cd /usr/ports
&prompt.root; make -DIS_INTERACTIVE install
to finish the job.
At work, we are using frobble , which
is in your Ports collection, but we have altered it quite a
bit to get it to do what we need. Is there any way of making
our own packages, so we can distribute it more easily around
our sites?
No problem, assuming you know how to make patches for
your changes:
&prompt.root; cd /usr/ports/somewhere/frobble
&prompt.root; make extract
&prompt.root; cd work/frobble-2.8
[Apply your patches]
&prompt.root; cd ../..
&prompt.root; make package
This ports stuff is really clever. I am desperate to
find out how you did it. What is the secret?
Nothing secret about it at all, just look at the
bsd.port.mk and
bsd.port.subdir.mk files in your
makefiles
directory.
(Readers with an aversion to intricate shell-scripts are
advised not to follow this link...)
Help! This port is broken!
If you come across a port that doesn't work for you, there are
a few things you can do, including:
Fix it! The how to make a
port
section should help you do this.
Gripe—by email only! Send
email to the maintainer of the port first. Type make
maintainer or read the Makefile
to find the maintainer's email address. Remember to include
the name and version of the port (send the
$FreeBSD: line from the
Makefile ) and the output leading up to the
error when you email the maintainer. If you do not get a
response from the maintainer, you can use
send-pr to submit a bug report.
Forget about it. This is the easiest route—very
few ports can be classified as essential
. There's
also a good chance any problems will be fixed in the next
version when the port is updated.
Grab the package from an ftp site near you. The
master
package collection is on ftp.FreeBSD.org in the packages
directory , but be sure to check your local mirror
first! These are more likely to work
than trying to compile from source and are a lot faster as
well. Use the &man.pkg.add.1; program to install the package
on your system.
Advanced Topics
The documentation that was here has been moved to its own Porter's Handbook for ease of
reference. Please go there if you wish to create and submit your own
ports.
diff --git a/en_US.ISO8859-1/books/handbook/ppp-and-slip/chapter.sgml b/en_US.ISO8859-1/books/handbook/ppp-and-slip/chapter.sgml
index b89c535ebe..98e7f71b45 100644
--- a/en_US.ISO8859-1/books/handbook/ppp-and-slip/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/ppp-and-slip/chapter.sgml
@@ -1,2778 +1,2778 @@
PPP and SLIP
Restructured, reorganized, and updated by &a.jim;,
1 March 2000.
Synopsis
If you are connecting to the Internet via modem, or wish to
provide dial-up connections to the Internet for others using FreeBSD,
you have the option of using PPP or SLIP.
This chapter covers three varieties of PPP;
user , kernel , and
PPPoE (PPP over Ethernet). It also covers
setting up a SLIP client and server.
The first variety of PPP that will be covered is User PPP. User
PPP was introduced into FreeBSD in 2.0.5-RELEASE as an addition to
the already existing kernel implementation of PPP.
You may be wondering what the main difference is between User
PPP and kernel PPP. The answer is simple; user PPP does not run as
a daemon, and can run as and when desired. No PPP interface needs
to be compiled into their kernel; it runs as a user process, and uses
the tunnel device driver (tun ) to get data
into and out of the kernel.
From here on out in this chapter, user ppp will simply be
referred to as ppp unless a distinction needs to be made between it
- and and any other PPP software such as pppd .
+ and any other PPP software such as pppd .
Unless otherwise stated, all of the commands explained in this
section should be executed as root.
Using User PPP
Originally contributed by &a.brian;, with input
from &a.nik;, &a.dirkvangulik;, and &a.pjc;.
User PPP
Assumptions
This document assumes you have the following:
An account with an Internet Service Provider (ISP) which
you connect to using PPP. Further, you have a modem or
other device connected to your system and configured
correctly, which allows you to connect to your ISP.
The dial-up number(s) of your ISP.
Your login name and password. This can be either a
regular UNIX-style login and password pair, or a PAP or CHAP
login and password pair.
The IP address(es) of one or more name servers.
Normally, you will be given two IP addresses by your ISP to
use for this. If they have not given you at least one, then
you can use the enable dns command in
your ppp.conf file to tell
ppp to set the name servers for
you.
The following information may be supplied by your ISP, but
is not completely necessary:
The IP address of your ISP's gateway. The gateway is
the machine to which you will connect and will be set up as
your default route . If you do not have
this information, we can make one up and your ISP's PPP
server will tell us the correct value when we connect.
This IP number is referred to as
HISADDR by
ppp .
The netmask you should use. If your ISP has not
provided you with one, you can safely use 255.255.255.0 .
If your ISP provides you with a static IP address and
hostname, you can enter it. Otherwise, we simply let the
peer assign whatever IP address it sees fit.
If you do not have any of the required information, contact
your ISP and make sure they provide it to you.
Preparing the Kernel
As previously mentioned, ppp
uses the tun device, and whichever kernel
you are using must have tun configured.
The tun device is preconfigured
for the default GENERIC kernel that ships
with FreeBSD. However, if you have installed a custom kernel,
you must make sure your kernel is configured for ppp.
To check, go to your kernel compile directory
(/sys/i386/conf or
/sys/pc98/conf ) and examine your
configuration file. It should have the following line somewhere
in it:
pseudo-device tun 1
If this line is not present, you will need to add it to the
configuration file and recompile your kernel. The stock
GENERIC kernel has this included, so if you
have not installed a custom kernel or do not have a
/sys directory, you do not have to change
anything. If you do need to recompile your kernel, please refer
to the kernel configuration
section for more information.
You can check how many tunnel devices your current kernel
has by typing the following:
&prompt.root; ifconfig -a
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
inet 200.10.100.1 --> 203.10.100.24 netmask 0xffffffff
tun1: flags=8050<POINTOPOINT,RUNNING,MULTICAST> mtu 576
tun2: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
inet 203.10.100.1 --> 203.10.100.20 netmask 0xffffffff
tun3: flags=8010<POINTOPOINT,MULTICAST> mtu 1500
In FreeBSD 4.0 and later releases, you will only see any
tun devices which have already been
used. This means you might not see any
tun devices. If this is the case, do
not worry; the device should be created dynamically when
ppp attempts to use it.
This case shows four tunnel devices, two of which are
currently configured and being used. It should be noted that
the RUNNING flag above indicates that the
interface has been used at some point—it is not an error
if your interface does not show up as
RUNNING .
If for some reason you have a kernel that does not have the
tun device in it and cannot recompile
the kernel, all is not lost. You should be able to dynamically
load the code. Please refer to the appropriate
&man.modload.8; and &man.lkm.4; man pages for further
details.
Check the tun device
Under normal circumstances, most users will only require one
tun device
(/dev/tun0 ). If you have specified more
than one on the pseudo-device line for
tun in your kernel configuration file,
then alter all references to tun0 below
to reflect whichever device number you are using (e.g.,
tun2 ).
The easiest way to make sure that the
tun0 device is configured correctly,
is to remake the device. This process is quite easy. To remake
the device, do the following:
&prompt.root; cd /dev
&prompt.root; ./MAKEDEV tun0
If you need 16 tunnel devices in your kernel, you will need
to create them. This can be done by executing the following
commands:
&prompt.root; cd /dev
&prompt.root; ./MAKEDEV tun15
To confirm that the kernel is configured correctly, issue
the follow command and compare the results:
&prompt.root; ifconfig tun0
tun0: flags=8050<POINTOPOINT,RUNNING,MULTICAST> mut 1500
The RUNNING flag may not yet be set, in
which case you will see:
&prompt.root; ifconfig tun0
tun0: flags=8010<POINTOPOINT,MULTICAST> mtu 1500
Remember from earlier that you might not see the device if it
has not been used yet, as tun devices are
created on demand in FreeBSD 4.0 and later releases.
Name Resolution Configuration
The resolver is the part of the system that turns IP
addresses into hostnames and vice versa. It can be configured
to look for maps that describe IP to hostname mappings in one of
two places. The first is a file called
/etc/hosts . Read &man.hosts.5; for more
information. The second is the Internet Domain Name Service
(DNS), a distributed data base, the discussion of which is
beyond the scope of this document.
The resolver is a set of system calls that do the name
mappings, but you have to tell them where to find their
information. You do this by first editing the file
/etc/host.conf . Do not
call this file /etc/hosts.conf (note the
extra s ) as the results can be
confusing.
Edit /etc/host.conf
This file should contain the following two lines (in this
order):
hosts
bind
These instruct the resolver to first look in the file
/etc/hosts , and then to consult the DNS
if the name was not found.
Edit /etc/hosts
This file should contain the IP addresses and names of
machines on your network. At a bare minimum it should contain
entries for the machine which will be running ppp. Assuming
that your machine is called foo.bar.com with the IP address 10.0.0.1 ,
/etc/hosts should contain:
127.0.0.1 localhost.bar.com localhost
127.0.0.1 localhost.bar.com.
10.0.0.1 foo.bar.com foo
10.0.0.1 foo.bar.com.
The first two lines define the alias
localhost as a synonym for the current
machine. Regardless of your own IP address, the IP address
for this line should always be 127.0.0.1 . The second two lines map
the name foo.bar.com (and the
shorthand foo ) to the IP address 10.0.0.1 .
If your provider allocates you a static IP address and
name, use them in place of the 10.0.0.1 entry.
Edit /etc/resolv.conf
The /etc/resolv.conf file tells the
resolver how to behave. If you are running your own DNS, you
may leave this file empty. Normally, you will need to enter
the following line(s):
domain bar.com
nameserver x.x.x.x
nameserver y.y.y.y
The x.x.x.x and
y.y.y.y
addresses are those given to you by your ISP. Add as many
nameserver lines as your ISP provides. The
domain line defaults to your hostname's
domain, and is probably unnecessary. Refer to the
&man.resolv.conf.5; manual page for details of other possible
entries in this file.
If you are running PPP version 2 or greater, the
enable dns command will tell PPP to request
that your ISP confirms the nameserver values. If your ISP
supplies different addresses (or if there are no nameserver
lines in /etc/resolv.conf ), PPP will
rewrite the file with the ISP-supplied values.
PPP Configuration
Both ppp and pppd
(the kernel level implementation of PPP) use the configuration
files located in the /usr/share/examples/ppp directory.
The sample configuration files provided are a good reference,
so do not delete them.
Configuring ppp requires that you edit a
number of files, depending on your requirements. What you put
in them depends to some extent on whether your ISP allocates IP
addresses statically (i.e., you get given one IP address, and
always use that one) or dynamically (i.e., your IP address
changes each time you connect to your ISP).
PPP and Static IP Addresses
You will need to create a configuration file called
/etc/ppp/ppp.conf . It should look
similar to the example below.
Lines that end in a : start in the
first column, all other lines should be indented as shown
using spaces or tabs.
1 default:
2 set device /dev/cuaa0
3 set speed 115200
4 set dial "ABORT BUSY ABORT NO\\sCARRIER TIMEOUT 5 \"\" ATE1Q0 OK-AT-OK \\dATDT\\TTIMEOUT 40 CONNECT"
5 provider:
6 set phone "(123) 456 7890"
7 set login "TIMEOUT 10 \"\" \"\" gin:--gin: foo word: bar col: ppp"
8 set timeout 300
9 set ifaddr x.x.x.x y.y.y.y 255.255.255.0 0.0.0.0
10 add default HISADDR
11 enable dns
Do not include the line numbers, they are just for
reference in this discussion.
Line 1:
Identifies the default entry. Commands in this
entry are executed automatically when ppp is run.
Line 2:
Identifies the device to which the modem is
connected. COM1 is
/dev/cuaa0 and
COM2 is
/dev/cuaa1 .
Line 3:
Sets the speed you want to connect at. If 115200
does not work (it should with any reasonably new modem),
try 38400 instead.
Line 4:
The dial string. User PPP uses an expect-send
syntax similar to the &man.chat.8; program. Refer to
the manual page for information on the features of this
language.
Line 5:
Identifies an entry for a provider called
provider
.
Line 6:
Sets the phone number for this provider. Multiple
phone numbers may be specified using the colon
(: ) or pipe character
(| )as a separator. The difference
between the two separators is described in &man.ppp.8;.
To summarize, if you want to rotate through the numbers,
use a colon. If you want to always attempt to dial the
first number first and only use the other numbers if the
first number fails, use the pipe character. Always
quote the entire set of phone numbers as shown.
Line 7:
The login string is of the same chat-like syntax as
the dial string. In this example, the string works for
a service whose login session looks like this:
J. Random Provider
login: foo
password: bar
protocol: ppp
You will need to alter this script to suit your own
needs. When you write this script for the first time,
you should enable chat
logging to ensure
that the conversation is going as expected.
If you are using PAP or CHAP, there will be no login
at this point, so your login string can be left blank.
See PAP and CHAP
authentication for further details.
Line 8:
Sets the default timeout (in seconds) for the
connection. Here, the connection will be closed
automatically after 300 seconds of inactivity. If you
never want to timeout, set this value to zero.
Line 9:
Sets the interface addresses. The string
x.x.x.x should be replaced by
the IP address that your provider has allocated to you.
The string y.y.y.y should be
replaced by the IP address that your ISP indicated for
their gateway (the machine to which you connect). If
your ISP hasn't given you a gateway address, use 10.0.0.2/0 . If you need to use
a guessed
address, make sure that you
create an entry in
/etc/ppp/ppp.linkup as per the
instructions for PPP
and Dynamic IP addresses. If this line is
omitted, ppp cannot run in
-auto or -dynamic
mode.
Line 10:
Adds a default route to your ISP's gateway. The
special word HISADDR is replaced with
the gateway address specified on line 9. It is
important that this line appears after line 9,
otherwise HISADDR will not yet be
initialized.
Line 11:
This line tells PPP to ask your ISP to confirm that
your nameserver addresses are correct. If your ISP
supports this facility, PPP can then update
/etc/resolv.conf with the correct
nameserver entries.
It is not necessary to add an entry to
ppp.linkup when you have a static IP
address as your routing table entries are already correct
before you connect. You may however wish to create an entry
to invoke programs after connection. This is explained later
with the sendmail example.
Example configuration files can be found in the
/usr/share/examples/ppp directory.
PPP and Dynamic IP Addresses
If your service provider does not assign static IP
addresses, ppp can be configured to
negotiate the local and remote addresses. This is done by
guessing
an IP address and allowing
ppp to set it up correctly using the IP
Configuration Protocol (IPCP) after connecting. The
ppp.conf configuration is the same as
PPP and Static IP
Addresses, with the following change:
9 set ifaddr 10.0.0.1/0 10.0.0.2/0 255.255.255.0
Again, do not include the line numbers, they are just for
reference. Indentation of at least one space is
required.
Line 9:
The number after the / character
is the number of bits of the address that ppp will
insist on. You may wish to use IP numbers more
appropriate to your circumstances, but the above example
will always work.
The last argument (0.0.0.0 ) tells
PPP to negotiate using address 0.0.0.0 rather than 10.0.0.1 . Do not use
0.0.0.0 as the first argument to
set ifaddr as it prevents PPP from
setting up an initial route in -auto
mode.
If you are running version 1.x of PPP, you will also need
to create an entry in /etc/ppp/ppp.linkup .
ppp.linkup is used after a connection has
been established. At this point, ppp will
know what IP addresses should really be
used. The following entry will delete the existing bogus
routes, and create correct ones:
1 provider:
2 delete ALL
3 add 0 0 HISADDR
Line 1:
On establishing a connection, ppp
will look for an entry in ppp.linkup
according to the following rules: First, try to match
the same label as we used in
ppp.conf . If that fails, look for
an entry for the IP address of our gateway. This entry
is a four-octet IP style label. If we still have not
found an entry, look for the MYADDR
entry.
Line 2:
This line tells ppp to delete all
of the existing routes for the acquired
tun interface (except the
direct route entry).
Line 3:
This line tells ppp to add a
default route that points to HISADDR .
HISADDR will be replaced with the IP
number of the gateway as negotiated in the IPCP.
See the pmdemand entry in the files
/usr/share/examples/ppp/ppp.conf.sample and
/usr/share/examples/ppp/ppp.linkup.sample for a
detailed example.
Version 2 of PPP introduces sticky routes
.
Any add or delete lines
that contain MYADDR or
HISADDR will be remembered, and any time
the actual values of MYADDR or
HISADDR change, the routes will be
reapplied. This removes the necessity of repeating these
lines in ppp.linkup .
Receiving Incoming Calls
When you configure ppp to
receive incoming calls on a machine connected to a LAN, you
must decide if you wish to forward packets to the LAN. If you
do, you should allocate the peer an IP number from your LAN's
subnet, and use the command enable proxy in
your /etc/ppp/ppp.conf file. You should
also confirm that the /etc/rc.conf file
contains the following:
gateway="YES"
Which getty?
Configuring FreeBSD for Dial-up
Services provides a good description on enabling
dial-up services using getty.
An alternative to getty is mgetty ,
a smarter version of getty designed with
dial-up lines in mind.
The advantages of using mgetty is
that it actively talks to modems,
meaning if port is turned off in
/etc/ttys then your modem will not answer
the phone.
Later versions of mgetty (from
0.99beta onwards) also support the automatic detection of
PPP streams, allowing your clients script-less access to
your server.
Refer to Mgetty and
AutoPPP for more information on
mgetty .
PPP Permissions
The ppp command must normally be run
as user id 0. If however, you wish to allow
ppp to run in server mode as a normal
user by executing ppp as described below,
that user must be given permission to run
ppp by adding them to the
network group in
/etc/group .
You will also need to give them access to one or more
sections of the configuration file using the
allow command:
allow users fred mary
If this command is used in the default
section, it gives the specified users access to
everything.
PPP Shells for Dynamic-IP Users
Create a file called
/etc/ppp/ppp-shell containing the
following:
#!/bin/sh
IDENT=`echo $0 | sed -e 's/^.*-\(.*\)$/\1/'`
CALLEDAS="$IDENT"
TTY=`tty`
if [ x$IDENT = xdialup ]; then
IDENT=`basename $TTY`
fi
echo "PPP for $CALLEDAS on $TTY"
echo "Starting PPP for $IDENT"
exec /usr/sbin/ppp -direct $IDENT
This script should be executable. Now make a symbolic
link called ppp-dialup to this script
using the following commands:
&prompt.root; ln -s ppp-shell /etc/ppp/ppp-dialup
You should use this script as the
shell for all of your dialup users.
This is an example from /etc/password
for a dialup PPP user with username
pchilds (remember don't directly edit
the password file, use vipw ).
pchilds:*:1011:300:Peter Childs PPP:/home/ppp:/etc/ppp/ppp-dialup
Create a /home/ppp directory that
is world readable containing the following 0 byte
files:
-r--r--r-- 1 root wheel 0 May 27 02:23 .hushlogin
-r--r--r-- 1 root wheel 0 May 27 02:22 .rhosts
which prevents /etc/motd from being
displayed.
PPP shells for Static-IP Users
Create the ppp-shell file as above
and for each account with statically assigned IPs create a
symbolic link to ppp-shell .
For example, if you have three dialup customers
fred , sam , and
mary , that you route class C networks
for, you would type the following:
&prompt.root; ln -s /etc/ppp/ppp-shell /etc/ppp/ppp-fred
&prompt.root; ln -s /etc/ppp/ppp-shell /etc/ppp/ppp-sam
&prompt.root; ln -s /etc/ppp/ppp-shell /etc/ppp/ppp-mary
Each of these users dialup accounts should have their
shell set to the symbolic link created above (i.e.,
mary 's shell should be
/etc/ppp/ppp-mary ).
Setting up ppp.conf for dynamic-IP users
The /etc/ppp/ppp.conf file should
contain something along the lines of:
default:
set debug phase lcp chat
set timeout 0
ttyd0:
set ifaddr 203.14.100.1 203.14.100.20 255.255.255.255
enable proxy
ttyd1:
set ifaddr 203.14.100.1 203.14.100.21 255.255.255.255
enable proxy
The indenting is important.
The default: section is loaded for
each session. For each dialup line enabled in
/etc/ttys create an entry similar to
the one for ttyd0: above. Each line
should get a unique IP address from your pool of IP
addresses for dynamic users.
Setting up ppp.conf for static-IP
users
Along with the contents of the sample
/usr/share/examples/ppp/ppp.conf above you should add
a section for each of the statically assigned dialup users.
We will continue with our fred ,
sam , and mary
example.
fred:
set ifaddr 203.14.100.1 203.14.101.1 255.255.255.255
sam:
set ifaddr 203.14.100.1 203.14.102.1 255.255.255.255
mary:
set ifaddr 203.14.100.1 203.14.103.1 255.255.255.255
The file /etc/ppp/ppp.linkup should
also contain routing information for each static IP user if
required. The line below would add a route for the 203.14.101.0 class C via the
client's ppp link.
fred:
add 203.14.101.0 netmask 255.255.255.0 HISADDR
sam:
add 203.14.102.0 netmask 255.255.255.0 HISADDR
mary:
add 203.14.103.0 netmask 255.255.255.0 HISADDR
More on mgetty , AutoPPP, and MS
extensions
mgetty and AutoPPP
Configuring and compiling mgetty with
the AUTO_PPP option enabled allows
mgetty to detect the LCP phase of PPP
connections and automatically spawn off a ppp shell.
However, since the default login/password sequence does not
occur it is necessary to authenticate users using either PAP
or CHAP.
This section assumes the user has successfully
configured, compiled, and installed a version of
mgetty with the
AUTO_PPP option (v0.99beta or
later).
Make sure your
/usr/local/etc/mgetty+sendfax/login.config
file has the following in it:
/AutoPPP/ - - /etc/ppp/ppp-pap-dialup
This will tell mgetty to run the
ppp-pap-dialup script for detected PPP
connections.
Create a file called
/etc/ppp/ppp-pap-dialup containing the
following (the file should be executable):
#!/bin/sh
exec /usr/sbin/ppp -direct pap$IDENT
For each dialup line enabled in
/etc/ttys , create a corresponding entry
in /etc/ppp/ppp.conf . This will
happily co-exist with the definitions we created
above.
pap:
enable pap
set ifaddr 203.14.100.1 203.14.100.20-203.14.100.40
enable proxy
Each user logging in with this method will need to have
a username/password in
/etc/ppp/ppp.secret file, or
alternatively add the following option to authenticate users
via PAP from /etc/password file.
enable passwdauth
If you wish to assign some users a static IP number, you
can specify the number as the third argument in
/etc/ppp/ppp.secret . See
/usr/share/examples/ppp/ppp.secret.sample for
examples.
MS extensions
It is possible to configure PPP to supply DNS and
NetBIOS nameserver addresses on demand.
To enable these extensions with PPP version 1.x, the
following lines might be added to the relevant section of
/etc/ppp/ppp.conf .
enable msext
set ns 203.14.100.1 203.14.100.2
set nbns 203.14.100.5
And for PPP version 2 and above:
accept dns
set dns 203.14.100.1 203.14.100.2
set nbns 203.14.100.5
This will tell the clients the primary and secondary
name server addresses, and a netbios nameserver host.
In version 2 and above, if the
set dns line is omitted, PPP will use the
values found in /etc/resolv.conf .
PAP and CHAP authentication
Some ISPs set their system up so that the authentication
part of your connection is done using either of the PAP or
CHAP authentication mechanisms. If this is the case, your ISP
will not give a login: prompt when you
connect, but will start talking PPP immediately.
PAP is less secure than CHAP, but security is not normally
an issue here as passwords, although being sent as plain text
with PAP, are being transmitted down a serial line only.
There's not much room for crackers to
eavesdrop
.
Referring back to the PPP
and Static IP addresses or PPP and Dynamic IP addresses
sections, the following alterations must be made:
7 set login
…
12 set authname MyUserName
13 set authkey MyPassword
As always, do not include the line numbers, they are just
for reference in this discussion. Indentation of at least one
space is required.
Line 7:
Your ISP will not normally require that you log into
the server if you're using PAP or CHAP. You must
therefore disable your set login
string.
Line 12:
This line specifies your PAP/CHAP user name. You
will need to insert the correct value for
MyUserName .
Line 13:
This line specifies your PAP/CHAP password. You
will need to insert the correct value for
MyPassword . You may want to
add an additional line, such as:
15 accept PAP
or
15 accept CHAP
to make it obvious that this is the intention, but
PAP and CHAP are both accepted by default.
Changing your ppp configuration on the
fly
It is possible to talk to the ppp
program while it is running in the background, but only if a
suitable diagnostic port has been set up. To do this, add the
following line to your configuration:
set server /var/run/ppp-tun%d DiagnosticPassword 0177
This will tell PPP to listen to the specified unix-domain
socket, asking clients for the specified password before
allowing access. The %d in the name is
replaced with the tun device number
that is in use.
Once a socket has been set up, the &man.pppctl.8; program
may be used in scripts that wish to manipulate the running
program.
Final system configuration
You now have ppp configured, but there
are a few more things to do before it is ready to work. They
all involve editing the /etc/rc.conf
file.
Working from the top down in this file, make sure the
hostname= line is set, e.g.:
hostname="foo.bar.com"
If your ISP has supplied you with a static IP address and
name, it's probably best that you use this name as your host
name.
Look for the network_interfaces variable.
If you want to configure your system to dial your ISP on demand,
make sure the tun0 device is added to
the list, otherwise remove it.
network_interfaces="lo0 tun0" ifconfig_tun0=
The ifconfig_tun0 variable should be
empty, and a file called
/etc/start_if.tun0 should be created.
This file should contain the line:
ppp -auto mysystem
This script is executed at network configuration time,
starting your ppp daemon in automatic mode. If you have a LAN
for which this machine is a gateway, you may also wish to use
the -alias switch. Refer to the manual page
for further details.
Set the router program to NO with
following line in your /etc/rc.conf :
router_enable="NO"
It is important that the routed daemon is
not started (it is started by default), as it
routed tends to delete the default routing
table entries created by ppp .
It is probably worth your while ensuring that the
sendmail_flags line does not include the
-q option, otherwise
sendmail will attempt to do a network lookup
every now and then, possibly causing your machine to dial out.
You may try:
sendmail_flags="-bd"
The downside of this is that you must force
sendmail to re-examine the mail queue
whenever the ppp link is up by typing:
&prompt.root; /usr/sbin/sendmail -q
You may wish to use the !bg command in
ppp.linkup to do this automatically:
1 provider:
2 delete ALL
3 add 0 0 HISADDR
4 !bg sendmail -bd -q30m
If you don't like this, it is possible to set up a
dfilter
to block SMTP traffic. Refer to the
sample files for further details.
Now the only thing left to do is reboot the machine.
All that is left is to reboot the machine. After rebooting,
you can now either type:
&prompt.root; ppp
and then dial provider to start the PPP
session, or, if you want ppp to establish
sessions automatically when there is outbound traffic (and
you have not created the start_if.tun0
script), type:
&prompt.root; ppp -auto provider
Summary
To recap, the following steps are necessary when setting up
ppp for the first time:
Client side:
Ensure that the tun device is
built into your kernel.
Ensure that the
tunX device
file is available in the /dev
directory.
Create an entry in
/etc/ppp/ppp.conf . The
pmdemand example should suffice for
most ISPs.
If you have a dynamic IP address, create an entry in
/etc/ppp/ppp.linkup .
Update your /etc/rc.conf
file.
Create a start_if.tun0 script if
you require demand dialing.
Server side:
Ensure that the tun device is
built into your kernel.
Ensure that the
tunX device
file is available in the /dev
directory.
Create an entry in /etc/passwd
(using the &man.vipw.8; program).
Create a profile in this users home directory that runs
ppp -direct direct-server or
similar.
Create an entry in
/etc/ppp/ppp.conf . The
direct-server example should
suffice.
Create an entry in
/etc/ppp/ppp.linkup .
Update your /etc/rc.conf
file.
Using Kernel PPP
Parts originally contributed by &a.gena; and
&a.rhuff;.
Setting up Kernel PPP
Before you start setting up PPP on your machine make sure
that pppd is located in
/usr/sbin and the directory
/etc/ppp exists.
pppd can work in two modes:
As a client
, i.e., you want to connect your
machine to the outside world via a PPP serial connection or
modem line.
as a server
, i.e. your machine is located on
the network and used to connect other computers using
PPP.
In both cases you will need to set up an options file
(/etc/ppp/options or
~/.ppprc if you have more than one user on
your machine that uses PPP).
You also will need some modem/serial software (preferably
kermit) so you can dial and establish a connection with the
remote host.
Using pppd as a client
The following /etc/ppp/options might be
used to connect to a CISCO terminal server PPP line.
crtscts # enable hardware flow control
modem # modem control line
noipdefault # remote PPP server must supply your IP address.
# if the remote host doesn't send your IP during IPCP
# negotiation , remove this option
passive # wait for LCP packets
domain ppp.foo.com # put your domain name here
:<remote_ip> # put the IP of remote PPP host here
# it will be used to route packets via PPP link
# if you didn't specified the noipdefault option
# change this line to <local_ip>:<remote_ip>
defaultroute # put this if you want that PPP server will be your
# default router
To connect:
Dial to the remote host using kermit (or some other modem
program), and enter your user name and password (or whatever
is needed to enable PPP on the remote host).
Exit kermit (without hanging up the line).
Enter the following:
&prompt.root; /usr/src/usr.sbin/pppd.new/pppd /dev/tty01 19200
Be sure to use the appropriate speed and device name.
Now your computer is connected with PPP. If the connection
fails, you can add the debug option to the
/etc/ppp/options file and check messages on
the console to track the problem.
Following /etc/ppp/pppup script will make
all 3 stages automatically:
#!/bin/sh
ps ax |grep pppd |grep -v grep
pid=`ps ax |grep pppd |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing pppd, PID=' ${pid}
kill ${pid}
fi
ps ax |grep kermit |grep -v grep
pid=`ps ax |grep kermit |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing kermit, PID=' ${pid}
kill -9 ${pid}
fi
ifconfig ppp0 down
ifconfig ppp0 delete
kermit -y /etc/ppp/kermit.dial
pppd /dev/tty01 19200
/etc/ppp/kermit.dial is a kermit script
that dials and makes all necessary authorization on the remote
host (an example of such a script is attached to the end of this
document).
Use the following /etc/ppp/pppdown script
to disconnect the PPP line:
#!/bin/sh
pid=`ps ax |grep pppd |grep -v grep|awk '{print $1;}'`
if [ X${pid} != "X" ] ; then
echo 'killing pppd, PID=' ${pid}
kill -TERM ${pid}
fi
ps ax |grep kermit |grep -v grep
pid=`ps ax |grep kermit |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing kermit, PID=' ${pid}
kill -9 ${pid}
fi
/sbin/ifconfig ppp0 down
/sbin/ifconfig ppp0 delete
kermit -y /etc/ppp/kermit.hup
/etc/ppp/ppptest
Check to see if PPP is still running by executing
/usr/etc/ppp/ppptest , which should look like
this:
#!/bin/sh
pid=`ps ax| grep pppd |grep -v grep|awk '{print $1;}'`
if [ X${pid} != "X" ] ; then
echo 'pppd running: PID=' ${pid-NONE}
else
echo 'No pppd running.'
fi
set -x
netstat -n -I ppp0
ifconfig ppp0
To hang up the modem, execute
/etc/ppp/kermit.hup , which should
contain:
set line /dev/tty01 ; put your modem device here
set speed 19200
set file type binary
set file names literal
set win 8
set rec pack 1024
set send pack 1024
set block 3
set term bytesize 8
set command bytesize 8
set flow none
pau 1
out +++
inp 5 OK
out ATH0\13
echo \13
exit
Here is an alternate method using chat
instead of kermit .
The following two files are sufficient to accomplish a pppd
connection.
/etc/ppp/options :
/dev/cuaa1 115200
crtscts # enable hardware flow control
modem # modem control line
connect "/usr/bin/chat -f /etc/ppp/login.chat.script"
noipdefault # remote PPP serve must supply your IP address.
# if the remote host doesn't send your IP during
# IPCP negotiation, remove this option
passive # wait for LCP packets
domain <your.domain> # put your domain name here
: # put the IP of remote PPP host here
# it will be used to route packets via PPP link
# if you didn't specified the noipdefault option
# change this line to <local_ip>:<remote_ip>
defaultroute # put this if you want that PPP server will be
# your default router
/etc/ppp/login.chat.script :
The following should go on a single line.
ABORT BUSY ABORT 'NO CARRIER' "" AT OK ATDT<phone.number>
CONNECT "" TIMEOUT 10 ogin:-\\r-ogin: <login-id>
TIMEOUT 5 sword: <password>
Once these are installed and modified correctly, all you need
to do is run pppd , like so:
&prompt.root; pppd
This sample is based primarily on information provided by:
Trev Roydhouse <Trev.Roydhouse@f401.n711.z3.fidonet.org>
and used with permission.
Using pppd as a server
/etc/ppp/options should contain something
similar to the following:
crtscts # Hardware flow control
netmask 255.255.255.0 # netmask ( not required )
192.114.208.20:192.114.208.165 # ip's of local and remote hosts
# local ip must be different from one
# you assigned to the ethernet ( or other )
# interface on your machine.
# remote IP is ip address that will be
# assigned to the remote machine
domain ppp.foo.com # your domain
passive # wait for LCP
modem # modem line
The following /etc/ppp/pppserv script
will enable tell pppd to behave as a
server:
#!/bin/sh
ps ax |grep pppd |grep -v grep
pid=`ps ax |grep pppd |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing pppd, PID=' ${pid}
kill ${pid}
fi
ps ax |grep kermit |grep -v grep
pid=`ps ax |grep kermit |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing kermit, PID=' ${pid}
kill -9 ${pid}
fi
# reset ppp interface
ifconfig ppp0 down
ifconfig ppp0 delete
# enable autoanswer mode
kermit -y /etc/ppp/kermit.ans
# run ppp
pppd /dev/tty01 19200
Use this /etc/ppp/pppservdown script to
stop the server:
#!/bin/sh
ps ax |grep pppd |grep -v grep
pid=`ps ax |grep pppd |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing pppd, PID=' ${pid}
kill ${pid}
fi
ps ax |grep kermit |grep -v grep
pid=`ps ax |grep kermit |grep -v grep|awk '{print $1;}'`
if [ "X${pid}" != "X" ] ; then
echo 'killing kermit, PID=' ${pid}
kill -9 ${pid}
fi
ifconfig ppp0 down
ifconfig ppp0 delete
kermit -y /etc/ppp/kermit.noans
The following kermit script
(/etc/ppp/kermit.ans ) will enable/disable
autoanswer mode on your modem. It should look like this:
set line /dev/tty01
set speed 19200
set file type binary
set file names literal
set win 8
set rec pack 1024
set send pack 1024
set block 3
set term bytesize 8
set command bytesize 8
set flow none
pau 1
out +++
inp 5 OK
out ATH0\13
inp 5 OK
echo \13
out ATS0=1\13 ; change this to out ATS0=0\13 if you want to disable
; autoanswer mod
inp 5 OK
echo \13
exit
A script named /etc/ppp/kermit.dial is
used for dialing and authenticating on the remote host. You will
need to customize it for your needs. Put your login and password
in this script; you will also need to change the input statement
depending on responses from your modem and remote host.
;
; put the com line attached to the modem here:
;
set line /dev/tty01
;
; put the modem speed here:
;
set speed 19200
set file type binary ; full 8 bit file xfer
set file names literal
set win 8
set rec pack 1024
set send pack 1024
set block 3
set term bytesize 8
set command bytesize 8
set flow none
set modem hayes
set dial hangup off
set carrier auto ; Then SET CARRIER if necessary,
set dial display on ; Then SET DIAL if necessary,
set input echo on
set input timeout proceed
set input case ignore
def \%x 0 ; login prompt counter
goto slhup
:slcmd ; put the modem in command mode
echo Put the modem in command mode.
clear ; Clear unread characters from input buffer
pause 1
output +++ ; hayes escape sequence
input 1 OK\13\10 ; wait for OK
if success goto slhup
output \13
pause 1
output at\13
input 1 OK\13\10
if fail goto slcmd ; if modem doesn't answer OK, try again
:slhup ; hang up the phone
clear ; Clear unread characters from input buffer
pause 1
echo Hanging up the phone.
output ath0\13 ; hayes command for on hook
input 2 OK\13\10
if fail goto slcmd ; if no OK answer, put modem in command mode
:sldial ; dial the number
pause 1
echo Dialing.
output atdt9,550311\13\10 ; put phone number here
assign \%x 0 ; zero the time counter
:look
clear ; Clear unread characters from input buffer
increment \%x ; Count the seconds
input 1 {CONNECT }
if success goto sllogin
reinput 1 {NO CARRIER\13\10}
if success goto sldial
reinput 1 {NO DIALTONE\13\10}
if success goto slnodial
reinput 1 {\255}
if success goto slhup
reinput 1 {\127}
if success goto slhup
if < \%x 60 goto look
else goto slhup
:sllogin ; login
assign \%x 0 ; zero the time counter
pause 1
echo Looking for login prompt.
:slloop
increment \%x ; Count the seconds
clear ; Clear unread characters from input buffer
output \13
;
; put your expected login prompt here:
;
input 1 {Username: }
if success goto sluid
reinput 1 {\255}
if success goto slhup
reinput 1 {\127}
if success goto slhup
if < \%x 10 goto slloop ; try 10 times to get a login prompt
else goto slhup ; hang up and start again if 10 failures
:sluid
;
; put your userid here:
;
output ppp-login\13
input 1 {Password: }
;
; put your password here:
;
output ppp-password\13
input 1 {Entering SLIP mode.}
echo
quit
:slnodial
echo \7No dialtone. Check the telephone line!\7
exit 1
; local variables:
; mode: csh
; comment-start: "; "
; comment-start-skip: "; "
; end:
Using PPP over Ethernet (PPPoE)
Contributed by &a.jim; (from node.to ) 10 Jan 2000.
The following describes how to set up PPP over Ethernet, a.k.a,
PPPoE.
Prerequisites
There are a few requirements that your system will need to meet
in order for PPPoE to function properly. They are:
Kernel source for FreeBSD 3.4 or later
ppp from FreeBSD 3.4 or later
Kernel Configuration
You will need to set the following options in your kernel
configuration file and then compile a new
kernel.
options NETGRAPH
Optionally, you can add
options NETGRAPH_PPPOE
options NETGRAPH_SOCKET
although if this functionality is not available at runtime,
ppp will load the relevant modules
on demand
Setting up ppp.conf
Here is an example of a working
ppp.conf :
default: # or name_of_service_provider
set device PPPoE:xl1 # replace xl1 with your ethernet device
set mru 1492
set mtu 1492
set authname YOURLOGINNAME
set authkey YOURPASSWORD
set log Phase tun command # you can add more detailed logging if you wish
set dial
set login
set ifaddr 10.0.0.1/0 10.0.0.2/0
add default HISADDR
nat enable yes # if you want to enable nat for your local net
papchap:
set authname YOURLOGINNAME
set authkey YOURPASSWORD
Care should be taken when running PPPoE with the
-nat option .
Running PPP
As root, you can run:
&prompt.root; ppp -ddial name_of_service_provider
Starting PPP at Boot
Add the following to your /etc/rc.conf
file:
ppp_enable="YES"
ppp_mode="ddial"
ppp_nat="YES"
ppp_profile="default" # or your provider
PPPoE with a 3Com HomeConnect ADSL Modem Dual Link
Contributed by &a.lioux;, 07 Apr
2001.
In short, it does not work. It should, but unfortunately,
that is not the case. For whatever reason, this modem does not
follow RFC
2516 (A Method for transmitting PPP over
Ethernet (PPPoE) , written by L. Mamakos, K. Lidl,
J. Evarts, D. Carrel, D. Simone, and R. Wheeler).
Since it does not follow the specification, FreeBSD's PPPoE
implementation will not talk to it. It is very likely that it will
not work under other unixes for that same reason. Complain to 3Com if you think it should
comply with the PPPoE specification.
If you absolutely want to use your ADSL connection with
FreeBSD and are stuck with this modem, you can either:
Try replacing the modem with a different brand or model
if your DSL provider permits you to do so. If you are not
sure which brand(s) will work, the &a.questions; is a good
place to ask.
Try to get it working. Keep in mind that there is no
guarantee it will work, your mileage may vary.
If you want to try to make it work, you can do the
following, but please keep in mind that you do this at
your own risk ! Just because it worked for me does
not mean it will work for you.
There are three steps to the process. They are:
Make sure you already have ppp.conf
set up. See the beginning of this chapter for more details
on doing so.
Since the modem does not speak the correct protocol, we
need to learn how to speak its variant of the protocol.
This information was obtained from a DSLreports
forum message .
The modem speaks 0x3c12 for
DISCOVERY , and 0x3c13
for PAYLOAD identifiers instead of
0x8863 and 0x8864
respectively, as mandated by the PPPoE specification.
Code
RFC's Code
Dual Link Modem's Code
PAYLOAD
0x8863
0x3c12
PAYLOAD
0x8864
0x3c13
So, now what? You need to recompile the
NETGRAPH_PPPOE code with the modem's
codes. For this, you should have installed the full kernel
sources.
Find the
/usr/src/sys/netgraph/ng_pppoe.h file.
Be careful while editing this file. You have to modify both
the little and the big endian entries.
For big endian, find the line with
0x8863 in it, and replace the number
with 0x3c12 . Do the same with
0x8864 , replacing it with
0x3c13 .
For little endian, find the line with
0x6388 in it, and replace the number
with 0x123c . Do the same with
0x6488 , replacing it with
0x133c .
Here is a diff of how the new file
should look:
&prompt.user; diff -u ng_pppoe.h.orig ng_pppoe.h
--- ng_pppoe.h.orig Thu Apr 12 13:42:46 2001
+++ ng_pppoe.h Thu Apr 12 13:44:47 2001
@@ -148,8 +148,8 @@
#define PTT_SYS_ERR (0x0202)
#define PTT_GEN_ERR (0x0203)
-#define ETHERTYPE_PPPOE_DISC 0x8863 /* pppoe discovery packets */
-#define ETHERTYPE_PPPOE_SESS 0x8864 /* pppoe session packets */
+#define ETHERTYPE_PPPOE_DISC 0x3c12 /* pppoe discovery packets */
+#define ETHERTYPE_PPPOE_SESS 0x3c13 /* pppoe session packets */
#else
#define PTT_EOL (0x0000)
#define PTT_SRV_NAME (0x0101)
@@ -162,8 +162,8 @@
#define PTT_SYS_ERR (0x0202)
#define PTT_GEN_ERR (0x0302)
-#define ETHERTYPE_PPPOE_DISC 0x6388 /* pppoe discovery packets */
-#define ETHERTYPE_PPPOE_SESS 0x6488 /* pppoe session packets */
+#define ETHERTYPE_PPPOE_DISC 0x123c /* pppoe discovery packets */
+#define ETHERTYPE_PPPOE_SESS 0x133c /* pppoe session packets */
#endif
struct pppoe_tag {
Then do the following as
root :
&prompt.root; cd /usr/src/sys/modules/netgraph/pppoe
&prompt.root; make clean depend all install
&prompt.root; make clean
Now you can speak the modem's variant of the PPPoE
specification.
The third step is to figure out the name of the profile
your ISP assigned to the modem. The information for this
step was obtained from the Roaring Penguin
PPPoE program which can be found in the ports collection. If you still are
not able to find it, ask your ISP's tech support.
If they do not know it either, and you are feeling bold
(this may de-program your modem and render it useless, so
think twice about doing it).
Install the program shipped with the modem by your
provider. Then, access the System menu
from the program. The name of your profile should be
listed there. It is usually ISP .
The profile name will be used in the PPPoE configuration
inside ppp.conf as the provider
parameter. See the &man.ppp.8; manual page for more
information.
The PPPoE line in your ppp.conf
should look like this:
set device PPPoE:xl1 :ISP
Do not forget to change xl1
to the proper device for your ethernet card.
Do not forget to change ISP
to the profile you have just found above.
For additional information, you can try:
Cheaper
Broadband with FreeBSD on DSL by Renaud
Waldura in Daemon
News .
Another PPPoE tutorial by Sympatico
Users Group .
Using SLIP
Originally contributed by &a.asami; and
&a.ghelmer;, with input from &a.wilko; and
&a.piero;.
Setting up a SLIP Client
The following is one way to set up a FreeBSD machine for SLIP
on a static host network. For dynamic hostname assignments (i.e.,
your address changes each time you dial up), you probably need to
do something much fancier.
First, determine which serial port your modem is connected to.
I have a symbolic link to /dev/modem from
/dev/cuaa1 , and only use the modem name in
my configuration files. It can become quite cumbersome when you
need to fix a bunch of files in /etc and
.kermrc 's all over the system!
/dev/cuaa0 is
COM1 , cuaa1 is
COM2 , etc.
Make sure you have the following in your kernel configuration
file:
pseudo-device sl 1
It is included in the GENERIC kernel, so
this should not be a problem unless you have deleted it.
Things you have to do only once
Add your home machine, the gateway and nameservers to
your /etc/hosts file. Mine looks like
this:
127.0.0.1 localhost loghost
136.152.64.181 silvia.HIP.Berkeley.EDU silvia.HIP silvia
136.152.64.1 inr-3.Berkeley.EDU inr-3 slip-gateway
128.32.136.9 ns1.Berkeley.edu ns1
128.32.136.12 ns2.Berkeley.edu ns2
Make sure you have hosts before
bind in your
/etc/host.conf . Otherwise, funny
things may happen.
Edit the /etc/rc.conf file.
Set your hostname by editing the line that
says:
hostname=myname.my.domain
You should give it your full Internet
hostname.
Add sl0 to the list of network interfaces by
changing the line that says:
network_interfaces="lo0"
to:
network_interfaces=lo0 sl0
Set the startup flags of sl0 by adding a
line:
ifconfig_sl0="inet ${hostname} slip-gateway netmask 0xffffff00 up"
Designate the default router by changing the
line:
defaultrouter=NO
to:
defaultrouter=slip-gateway
Make a file /etc/resolv.conf which
contains:
domain HIP.Berkeley.EDU
nameserver 128.32.136.9
nameserver 128.32.136.12
As you can see, these set up the nameserver hosts. Of
course, the actual domain names and addresses depend on your
environment.
Set the password for root and toor (and any other
accounts that do not have a password). Use passwd or
&man.vipw.8;, do not edit the
/etc/passwd or
/etc/master.passwd files!
Reboot your machine and make sure it comes up with the
correct hostname.
Making a SLIP connection
Dial up, type slip at the prompt,
enter your machine name and password. The things you need
to enter depends on your environment. If you use kermit, you
can try a script like this:
# kermit setup
set modem hayes
set line /dev/modem
set speed 115200
set parity none
set flow rts/cts
set terminal bytesize 8
set file type binary
# The next macro will dial up and login
define slip dial 643-9600, input 10 =>, if failure stop, -
output slip\x0d, input 10 Username:, if failure stop, -
output silvia\x0d, input 10 Password:, if failure stop, -
output ***\x0d, echo \x0aCONNECTED\x0a
Of course, you have to change the hostname and password
to fit yours. After doing so, you can just type
slip from the kermit prompt to get
connected.
Leaving your password in plain text anywhere in the
filesystem is generally a BAD idea. Do it at your own
risk.
Leave the kermit there (you can suspend it by
z ) and as root, type:
&prompt.root; slattach -h -c -s 115200 /dev/modem
If you are able to ping hosts on the
other side of the router, you are connected! If it does not
work, you might want to try -a instead of
-c as an argument to slattach.
How to shutdown the connection
Do the following:
&prompt.root; kill -INT `cat /var/run/slattach.modem.pid`
to kill slattach. Keep in mind you must be
root to do the above. Then go back to
kermit (fg if you suspended it) and exit from
it (q ).
The slattach man page says you have to use ifconfig
sl0 down to mark the interface down, but this does not
seem to make any difference for me.
(ifconfig sl0 reports the same thing.)
Some times, your modem might refuse to drop the carrier
(mine often does). In that case, simply start kermit and quit
it again. It usually goes out on the second try.
Troubleshooting
If it does not work, feel free to ask me. The things that
people tripped over so far:
Not using -c or -a in
slattach (I have no idea why this can be fatal, but adding
this flag solved the problem for at least one
person).
Using s10 instead of
sl0 (might be hard to see the difference on
some fonts).
Try ifconfig sl0 to see your
interface status. For example, you might get:
&prompt.root; ifconfig sl0
sl0: flags=10<POINTOPOINT>
inet 136.152.64.181 --> 136.152.64.1 netmask ffffff00
Also, netstat -r will give the
routing table, in case you get the no route to
host
messages from ping. Mine looks like:
&prompt.root; netstat -r
Routing tables
Destination Gateway Flags Refs Use IfaceMTU Rtt Netmasks:
(root node)
(root node)
Route Tree for Protocol Family inet:
(root node) =>
default inr-3.Berkeley.EDU UG 8 224515 sl0 - -
localhost.Berkel localhost.Berkeley UH 5 42127 lo0 - 0.438
inr-3.Berkeley.E silvia.HIP.Berkele UH 1 0 sl0 - -
silvia.HIP.Berke localhost.Berkeley UGH 34 47641234 lo0 - 0.438
(root node)
This is after transferring a bunch of files, your
numbers should be smaller).
Setting up a SLIP Server
This document provides suggestions for setting up SLIP Server
services on a FreeBSD system, which typically means configuring
your system to automatically startup connections upon login for
remote SLIP clients. The author has written this document based
on his experience; however, as your system and needs may be
different, this document may not answer all of your questions, and
the author cannot be responsible if you damage your system or lose
data due to attempting to follow the suggestions here.
Prerequisites
This document is very technical in nature, so background
knowledge is required. It is assumed that you are familiar with
the TCP/IP network protocol, and in particular, network and node
addressing, network address masks, subnetting, routing, and
routing protocols, such as RIP. Configuring SLIP services on a
dial-up server requires a knowledge of these concepts, and if
you are not familiar with them, please read a copy of either
Craig Hunt's TCP/IP Network Administration
published by O'Reilly & Associates, Inc. (ISBN Number
0-937175-82-X), or Douglas Comer's books on the TCP/IP
protocol.
It is further assumed that you have already setup your
modem(s) and configured the appropriate system files to allow
logins through your modems. If you have not prepared your
system for this yet, please see the tutorial for configuring
dialup services; if you have a World-Wide Web browser available,
browse the list of tutorials at http://www.FreeBSD.org/ .
You may also want to check the manual pages for &man.sio.4; for
information on the serial port device driver and &man.ttys.5;,
&man.gettytab.5;, &man.getty.8;, & &man.init.8; for
information relevant to configuring the system to accept logins
on modems, and perhaps &man.stty.1; for information on setting
serial port parameters (such as clocal for
directly-connected serial interfaces).
Quick Overview
In its typical configuration, using FreeBSD as a SLIP server
works as follows: a SLIP user dials up your FreeBSD SLIP Server
system and logs in with a special SLIP login ID that uses
/usr/sbin/sliplogin as the special user's
shell. The sliplogin program browses the
file /etc/sliphome/slip.hosts to find a
matching line for the special user, and if it finds a match,
connects the serial line to an available SLIP interface and then
runs the shell script
/etc/sliphome/slip.login to configure the
SLIP interface.
An Example of a SLIP Server Login
For example, if a SLIP user ID were
Shelmerg , Shelmerg 's
entry in /etc/master.passwd would look
something like this (except it would be all on one
line):
Shelmerg:password:1964:89::0:0:Guy Helmer - SLIP:/usr/users/Shelmerg:/usr/sbin/sliplogin
When Shelmerg logs in,
sliplogin will search
/etc/sliphome/slip.hosts for a line that
had a matching user ID; for example, there may be a line in
/etc/sliphome/slip.hosts that
reads:
Shelmerg dc-slip sl-helmer 0xfffffc00 autocomp
sliplogin will find that matching line,
hook the serial line into the next available SLIP interface,
and then execute /etc/sliphome/slip.login
like this:
/etc/sliphome/slip.login 0 19200 Shelmerg dc-slip sl-helmer 0xfffffc00 autocomp
If all goes well,
/etc/sliphome/slip.login will issue an
ifconfig for the SLIP interface to which
sliplogin attached itself (slip interface
0,in the above example, which was the first parameter in the
list given to slip.login ) to set the
local IP address (dc-slip ), remote IP address
(sl-helmer ), network mask for the SLIP
interface (0xfffffc00 ), and
any additional flags (autocomp ). If
something goes wrong, sliplogin usually
logs good informational messages via the
daemon syslog facility, which usually goes
into /var/log/messages (see the manual
pages for &man.syslogd.8; and &man.syslog.conf.5; and perhaps
check /etc/syslog.conf to see to which
files syslogd is logging).
OK, enough of the examples — let us dive into
setting up the system.
Kernel Configuration
FreeBSD's default kernels usually come with two SLIP
interfaces defined (sl0 and
sl1 ); you can use netstat
-i to see whether these interfaces are defined in your
kernel.
Sample output from netstat -i :
Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll
ed0 1500 <Link>0.0.c0.2c.5f.4a 291311 0 174209 0 133
ed0 1500 138.247.224 ivory 291311 0 174209 0 133
lo0 65535 <Link> 79 0 79 0 0
lo0 65535 loop localhost 79 0 79 0 0
sl0* 296 <Link> 0 0 0 0 0
sl1* 296 <Link> 0 0 0 0 0
The sl0 and
sl1 interfaces shown in
netstat -i 's output indicate that there are
two SLIP interfaces built into the kernel. (The asterisks after
the sl0 and sl1 indicate
that the interfaces are down
.)
However, FreeBSD's default kernels do not come configured
to forward packets (ie, your FreeBSD machine will not act as a
router) due to Internet RFC requirements for Internet hosts (see
RFCs 1009 [Requirements for Internet Gateways], 1122
[Requirements for Internet Hosts — Communication Layers],
and perhaps 1127 [A Perspective on the Host Requirements RFCs]),
so if you want your FreeBSD SLIP Server to act as a router, you
will have to edit the /etc/rc.conf file and
change the setting of the gateway_enable variable to
YES .
You will then need to reboot for the new settings to take
effect.
You will notice that near the end of the default kernel
configuration file (/sys/i386/conf/GENERIC )
is a line that reads:
pseudo-device sl 2
This is the line that defines the number of SLIP devices
available in the kernel; the number at the end of the line is
the maximum number of SLIP connections that may be operating
simultaneously.
Please refer to Configuring the
FreeBSD Kernel for help in reconfiguring your
kernel.
Sliplogin Configuration
As mentioned earlier, there are three files in the
/etc/sliphome directory that are part of
the configuration for /usr/sbin/sliplogin
(see &man.sliplogin.8; for the actual manual page for
sliplogin ): slip.hosts ,
which defines the SLIP users & their associated IP
addresses; slip.login , which usually just
configures the SLIP interface; and (optionally)
slip.logout , which undoes
slip.login 's effects when the serial
connection is terminated.
slip.hosts Configuration
/etc/sliphome/slip.hosts contains
lines which have at least four items, separated by
whitespace:
SLIP user's login ID
Local address (local to the SLIP server) of the SLIP
link
Remote address of the SLIP link
Network mask
The local and remote addresses may be host names (resolved
to IP addresses by /etc/hosts or by the
domain name service, depending on your specifications in
/etc/host.conf ), and the
network mask may be a name that can be resolved by a lookup
into /etc/networks . On a sample system,
/etc/sliphome/slip.hosts looks like
this:
#
# login local-addr remote-addr mask opt1 opt2
# (normal,compress,noicmp)
#
Shelmerg dc-slip sl-helmerg 0xfffffc00 autocomp
At the end of the line is one or more of the
options.
normal — no header
compression
compress — compress
headers
autocomp — compress headers if
the remote end allows it
noicmp — disable ICMP packets
(so any ping
packets will be dropped instead
of using up your bandwidth)
Note that sliplogin under early releases
of FreeBSD 2 ignored the options that FreeBSD 1.x recognized,
so the options normal ,
compress , autocomp , and
noicmp had no effect until support was added
in FreeBSD 2.2 (unless your slip.login
script included code to make use of the flags).
Your choice of local and remote addresses for your SLIP
links depends on whether you are going to dedicate a TCP/IP
subnet or if you are going to use proxy ARP
on
your SLIP server (it is not true
proxy ARP, but
that is the terminology used in this document to describe it).
If you are not sure which method to select or how to assign IP
addresses, please refer to the TCP/IP books referenced in the
slips-prereqs section
and/or consult your IP network manager.
If you are going to use a separate subnet for your SLIP
clients, you will need to allocate the subnet number out of
your assigned IP network number and assign each of your SLIP
client's IP numbers out of that subnet. Then, you will
probably either need to configure a static route to the SLIP
subnet via your SLIP server on your nearest IP router, or
install gated on your FreeBSD SLIP server
and configure it to talk the appropriate routing protocols to
your other routers to inform them about your SLIP server's
route to the SLIP subnet.
Otherwise, if you will use the proxy ARP
method, you will need to assign your SLIP client's IP
addresses out of your SLIP server's Ethernet subnet, and you
will also need to adjust your
/etc/sliphome/slip.login and
/etc/sliphome/slip.logout scripts to use
&man.arp.8; to manage the proxy-ARP entries in the SLIP
server's ARP table.
slip.login Configuration
The typical /etc/sliphome/slip.login
file looks like this:
#!/bin/sh -
#
# @(#)slip.login 5.1 (Berkeley) 7/1/90
#
# generic login file for a slip line. sliplogin invokes this with
# the parameters:
# 1 2 3 4 5 6 7-n
# slipunit ttyspeed loginname local-addr remote-addr mask opt-args
#
/sbin/ifconfig sl$1 inet $4 $5 netmask $6
This slip.login file merely
ifconfig 's the appropriate SLIP interface
with the local and remote addresses and network mask of the
SLIP interface.
If you have decided to use the proxy ARP
method (instead of using a separate subnet for your SLIP
clients), your /etc/sliphome/slip.login
file will need to look something like this:
#!/bin/sh -
#
# @(#)slip.login 5.1 (Berkeley) 7/1/90
#
# generic login file for a slip line. sliplogin invokes this with
# the parameters:
# 1 2 3 4 5 6 7-n
# slipunit ttyspeed loginname local-addr remote-addr mask opt-args
#
/sbin/ifconfig sl$1 inet $4 $5 netmask $6
# Answer ARP requests for the SLIP client with our Ethernet addr
/usr/sbin/arp -s $5 00:11:22:33:44:55 pub
The additional line in this
slip.login , arp -s
$5 00:11:22:33:44:55 pub , creates an ARP entry
in the SLIP server's ARP table. This ARP entry causes the
SLIP server to respond with the SLIP server's Ethernet MAC
address whenever a another IP node on the Ethernet asks to
speak to the SLIP client's IP address.
When using the example above, be sure to replace the
Ethernet MAC address (00:11:22:33:44:55 ) with the MAC address of
your system's Ethernet card, or your proxy ARP
will definitely not work! You can discover your SLIP server's
Ethernet MAC address by looking at the results of running
netstat -i ; the second line of the output
should look something like:
ed0 1500 <Link>0.2.c1.28.5f.4a 191923 0 129457 0 116
This indicates that this particular system's Ethernet MAC
address is 00:02:c1:28:5f:4a
— the periods in the Ethernet MAC address given by
netstat -i must be changed to colons and
leading zeros should be added to each single-digit hexadecimal
number to convert the address into the form that &man.arp.8;
desires; see the manual page on &man.arp.8; for complete
information on usage.
When you create
/etc/sliphome/slip.login and
/etc/sliphome/slip.logout , the
execute
bit (ie, chmod 755
/etc/sliphome/slip.login /etc/sliphome/slip.logout )
must be set, or sliplogin will be unable
to execute it.
slip.logout Configuration
/etc/sliphome/slip.logout is not
strictly needed (unless you are implementing proxy
ARP
), but if you decide to create it, this is an
example of a basic
slip.logout script:
#!/bin/sh -
#
# slip.logout
#
# logout file for a slip line. sliplogin invokes this with
# the parameters:
# 1 2 3 4 5 6 7-n
# slipunit ttyspeed loginname local-addr remote-addr mask opt-args
#
/sbin/ifconfig sl$1 down
If you are using proxy ARP
, you will want to
have /etc/sliphome/slip.logout remove the
ARP entry for the SLIP client:
#!/bin/sh -
#
# @(#)slip.logout
#
# logout file for a slip line. sliplogin invokes this with
# the parameters:
# 1 2 3 4 5 6 7-n
# slipunit ttyspeed loginname local-addr remote-addr mask opt-args
#
/sbin/ifconfig sl$1 down
# Quit answering ARP requests for the SLIP client
/usr/sbin/arp -d $5
The arp -d $5 removes the ARP entry
that the proxy ARP
slip.login added when the SLIP client
logged in.
It bears repeating: make sure
/etc/sliphome/slip.logout has the execute
bit set for after you create it (ie, chmod 755
/etc/sliphome/slip.logout ).
Routing Considerations
If you are not using the proxy ARP
method for
routing packets between your SLIP clients and the rest of your
network (and perhaps the Internet), you will probably either
have to add static routes to your closest default router(s) to
route your SLIP client subnet via your SLIP server, or you will
probably need to install and configure gated
on your FreeBSD SLIP server so that it will tell your routers
via appropriate routing protocols about your SLIP subnet.
Static Routes
Adding static routes to your nearest default routers can
be troublesome (or impossible, if you do not have authority to
do so...). If you have a multiple-router network in your
organization, some routers, such as Cisco and Proteon, may
not only need to be configured with the static route to the
SLIP subnet, but also need to be told which static routes to
tell other routers about, so some expertise and
troubleshooting/tweaking may be necessary to get
static-route-based routing to work.
Running gated
An alternative to the headaches of static routes is to
install gated on your FreeBSD SLIP server
and configure it to use the appropriate routing protocols
(RIP/OSPF/BGP/EGP) to tell other routers about your SLIP
subnet. You can use gated from the ports collection or retrieve and build
it yourself from the
GateD anonymous ftp site ; the current version
as of this writing is
gated-R3_5Alpha_8.tar.Z , which includes
support for FreeBSD out-of-the-box
. Complete
information and documentation on gated is
available on the Web starting at the Merit GateD
Consortium . Compile and install it, and then write a
/etc/gated.conf file to configure your
gated; here is a sample, similar to what the author used on a
FreeBSD SLIP server:
#
# gated configuration file for dc.dsu.edu; for gated version 3.5alpha5
# Only broadcast RIP information for xxx.xxx.yy out the ed Ethernet interface
#
#
# tracing options
#
traceoptions "/var/tmp/gated.output" replace size 100k files 2 general ;
rip yes {
interface sl noripout noripin ;
interface ed ripin ripout version 1 ;
traceoptions route ;
} ;
#
# Turn on a bunch of tracing info for the interface to the kernel:
kernel {
traceoptions remnants request routes info interface ;
} ;
#
# Propagate the route to xxx.xxx.yy out the Ethernet interface via RIP
#
export proto rip interface ed {
proto direct {
xxx.xxx.yy mask 255.255.252.0 metric 1; # SLIP connections
} ;
} ;
#
# Accept routes from RIP via ed Ethernet interfaces
import proto rip interface ed {
all ;
} ;
The above sample gated.conf file
broadcasts routing information regarding the SLIP subnet
xxx.xxx.yy via RIP onto the
Ethernet; if you are using a different Ethernet driver than
the ed driver, you will need to
change the references to the ed
interface appropriately. This sample file also sets up
tracing to /var/tmp/gated.output for
debugging gated 's activity; you can
certainly turn off the tracing options if
gated works OK for you. You will need to
change the xxx.xxx.yy 's into the
network address of your own SLIP subnet (be sure to change the
net mask in the proto direct clause as
well).
When you get gated built and installed
and create a configuration file for it, you will need to run
gated in place of routed
on your FreeBSD system; change the
routed/gated startup parameters in
/etc/netstart as appropriate for your
system. Please see the manual page for
gated for information on
gated 's command-line parameters.
diff --git a/en_US.ISO8859-1/books/handbook/printing/chapter.sgml b/en_US.ISO8859-1/books/handbook/printing/chapter.sgml
index 011f36c05a..ebedc1631d 100644
--- a/en_US.ISO8859-1/books/handbook/printing/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/printing/chapter.sgml
@@ -1,4565 +1,4565 @@
Printing
Contributed by &a.kelly;, 30 September 1995.
Restructured and updated by &a.jim;, March 2000.
Synopsis
In order to use printers with FreeBSD, you will need to set them
up to work with the Berkeley line printer spooling system, also
known as the LPD spooling system. It is the standard printer
control system in FreeBSD. This chapter introduces the LPD spooling
system, often simply called LPD, and will guide you through its
configuration.
If you are already familiar with LPD or another printer spooling
system, you may wish to skip to section Setting up the spooling
system.
Introduction
LPD controls everything about a host's printers. It is
responsible for a number of things:
It controls access to attached printers and printers
attached to other hosts on the network.
It enables users to submit files to be printed; these
submissions are known as jobs .
It prevents multiple users from accessing a printer at the
same time by maintaining a queue for each
printer.
It can print header pages (also known
as banner or burst
pages) so users can easily find jobs they have printed in a
stack of printouts.
It takes care of communications parameters for printers
connected on serial ports.
It can send jobs over the network to a LPD spooler on
another host.
It can run special filters to format jobs to be printed for
various printer languages or printer capabilities.
It can account for printer usage.
Through a configuration file
(/etc/printcap ), and by providing the special
filter programs, you can enable the LPD system to do all or some
subset of the above for a great variety of printer hardware.
Why You Should Use the Spooler
If you are the sole user of your system, you may be wondering
why you should bother with the spooler when you do not need access
control, header pages, or printer accounting. While it is
possible to enable direct access to a printer, you should use the
spooler anyway since:
LPD prints jobs in the background; you do not have to wait
for data to be copied to the printer.
LPD can conveniently run a job to be printed through
filters to add date/time headers or convert a special file
format (such as a TeX DVI file) into a format the printer will
understand. You will not have to do these steps
manually.
Many free and commercial programs that provide a print
feature usually expect to talk to the spooler on your system.
By setting up the spooling system, you will more easily
support other software you may later add or already
have.
Basic Setup
To use printers with the LPD spooling system, you will need to
set up both your printer hardware and the LPD software. This
document describes two levels of setup:
See section Simple Printer
Setup to learn how to connect a printer, tell LPD how to
communicate with it, and print plain text files to the
printer.
See section Advanced
Printer Setup to find out how to print a variety of
special file formats, to print header pages, to print across a
network, to control access to printers, and to do printer
accounting.
Simple Printer Setup
This section tells how to configure printer hardware and the
LPD software to use the printer. It teaches the basics:
Section Hardware
Setup gives some hints on connecting the printer to a
port on your computer.
Section Software
Setup shows how to setup the LPD spooler configuration
file (/etc/printcap ).
If you are setting up a printer that uses a network protocol
to accept data to print instead of a serial or parallel interface,
see Printers With
Networked Data Stream Interfaces.
Although this section is called Simple Printer
Setup
, it is actually fairly complex. Getting the printer
to work with your computer and the LPD spooler is the hardest
part. The advanced options like header pages and accounting are
fairly easy once you get the printer working.
Hardware Setup
This section tells about the various ways you can connect a
printer to your PC. It talks about the kinds of ports and
cables, and also the kernel configuration you may need to enable
FreeBSD to speak to the printer.
If you have already connected your printer and have
successfully printed with it under another operating system, you
can probably skip to section Software Setup.
Ports and Cables
Nearly all printers you can get for a PC today support one
or both of the following interfaces:
Serial interfaces use a serial
port on your computer to send data to the printer. Serial
interfaces are common in the computer industry and cables
are readily available and also easy to construct. Serial
interfaces sometimes need special cables and might require
you to configure somewhat complex communications
options.
Parallel interfaces use a
parallel port on your computer to send data to the
printer. Parallel interfaces are common in the PC market.
Cables are readily available but more difficult to
construct by hand. There are usually no communications
options with parallel interfaces, making their
configuration exceedingly simple.
Parallel interfaces are sometimes known as
Centronics
interfaces, named after the
connector type on the printer.
In general, serial interfaces are slower than parallel
interfaces. Parallel interfaces usually offer just
one-way communication (computer to printer) while serial
gives you two-way. Many newer parallel ports and printers
can communicate in both directions under FreeBSD when a
IEEE1284 compliant cable is used.
Usually, the only time you need two-way communication with
the printer is if the printer speaks PostScript. PostScript
printers can be very verbose. In fact, PostScript jobs are
actually programs sent to the printer; they need not produce
paper at all and may return results directly to the computer.
PostScript also uses two-way communication to tell the
computer about problems, such as errors in the PostScript
program or paper jams. Your users may be appreciative of such
information. Furthermore, the best way to do effective
accounting with a PostScript printer requires two-way
communication: you ask the printer for its page count (how
many pages it has printed in its lifetime), then send the
user's job, then ask again for its page count. Subtract the
two values and you know how much paper to charge the
user.
Parallel Ports
To hook up a printer using a parallel interface, connect
the Centronics cable between the printer and the computer.
The instructions that came with the printer, the computer, or
both should give you complete guidance.
Remember which parallel port you used on the computer.
The first parallel port is /dev/lpt0 to
FreeBSD; the second is /dev/lpt1 , and so
on.
Serial Ports
To hook up a printer using a serial interface, connect the
proper serial cable between the printer and the computer. The
instructions that came with the printer, the computer, or both
should give you complete guidance.
If you are unsure what the proper serial
cable
is, you may wish to try one of the following
alternatives:
A modem cable connects each pin
of the connector on one end of the cable straight through
to its corresponding pin of the connector on the other
end. This type of cable is also known as a
DTE-to-DCE
cable.
A null-modem cable connects some
pins straight through, swaps others (send data to receive
data, for example), and shorts some internally in each
connector hood. This type of cable is also known as a
DTE-to-DTE
cable.
A serial printer cable, required
for some unusual printers, is like the null modem cable,
but sends some signals to their counterparts instead of
being internally shorted.
You should also set up the communications parameters for
the printer, usually through front-panel controls or DIP
switches on the printer. Choose the highest
bps (bits per second, sometimes
baud rate ) rate that both your computer
and the printer can support. Choose 7 or 8 data bits; none,
even, or odd parity; and 1 or 2 stop bits. Also choose a flow
control protocol: either none, or XON/XOFF (also known as
in-band
or software
) flow control.
Remember these settings for the software configuration that
follows.
Software Setup
This section describes the software setup necessary to print
with the LPD spooling system in FreeBSD.
Here is an outline of the steps involved:
Configure your kernel, if necessary, for the port you
are using for the printer; section Kernel Configuration tells
you what you need to do.
Set the communications mode for the parallel port, if
you are using a parallel port; section Setting the
Communication Mode for the Parallel Port gives
details.
Test if the operating system can send data to the printer.
Section Checking Printer
Communications gives some suggestions on how to do
this.
Set up LPD for the printer by modifying the file
/etc/printcap . You will find out how
to do this later in this chapter.
Kernel Configuration
The operating system kernel is compiled to work with a
specific set of devices. The serial or parallel interface for
your printer is a part of that set. Therefore, it might be
necessary to add support for an additional serial or parallel
port if your kernel is not already configured for one.
To find out if the kernel you are currently using supports
a serial interface, type:
&prompt.root; dmesg | grep sioN
Where N is the number of the
serial port, starting from zero. If you see output similar to
the following:
sio2 at 0x3e8-0x3ef irq 5 on isa
sio2: type 16550A
then the kernel supports the port.
To find out if the kernel supports a parallel interface,
type:
&prompt.root; dmesg | grep lptN
Where N is the number of the
parallel port, starting from zero. If you see output similar
to the following lpt0 at 0x378-0x37f on isa
then the kernel supports the port.
You might have to reconfigure your kernel in order for the
operating system to recognize and use the parallel or serial
port you are using for the printer.
To add support for a serial port, see the section on
kernel configuration. To add support for a parallel port, see
that section and the section that
follows.
Adding /dev Entries for the
Ports
Even though the kernel may support communication along a
serial or parallel port, you will still need a software
interface through which programs running on the system can
send and receive data. That is what entries in the
/dev directory are for.
To add a /dev entry for a
port:
Become root with the &man.su.1; command. Enter the
root password when prompted.
Change to the /dev
directory:
&prompt.root; cd /dev
Type:
&prompt.root; ./MAKEDEV port
Where port is the device
entry for the port you want to make. Use
lpt0 for the first parallel port,
lpt1 for the second, and so on; use
ttyd0 for the first serial port,
ttyd1 for the second, and so on.
Type:
&prompt.root; ls -l port
to make sure the device entry got created.
Setting the Communication Mode for the Parallel
Port
When you are using the parallel interface, you can choose
whether FreeBSD should use interrupt-driven or polled
communication with the printer.
The interrupt-driven method is
the default with the GENERIC kernel. With this method,
the operating system uses an IRQ line to determine when
the printer is ready for data.
The polled method directs the
operating system to repeatedly ask the printer if it is
ready for more data. When it responds ready, the kernel
sends more data.
The interrupt-driven method is somewhat faster but uses up
a precious IRQ line. You should use whichever one
works.
You can set the communications mode in two ways: by
configuring the kernel or by using the &man.lptcontrol.8;
program.
To set the communications mode by configuring
the kernel:
Edit your kernel configuration file. Look for or add
an lpt0 entry. If you are setting up
the second parallel port, use lpt1
instead. Use lpt2 for the third port,
and so on.
If you want interrupt-driven mode, add the
irq specifier:
device lpt0 at isa? port? tty irq N vector lptintr
Where N is the IRQ
number for your computer's parallel port.
If you want polled mode, do not add the
irq specifier:
device lpt0 at isa? port? tty vector lptintr
Save the file. Then configure, build, and install the
kernel, then reboot. See kernel configuration for
more details.
To set the communications mode with
&man.lptcontrol.8;:
Type:
&prompt.root; lptcontrol -i -u N
to set interrupt-driven mode for
lptN .
Type:
&prompt.root; lptcontrol -p -u N
to set polled-mode for
lptN .
You could put these commands in your
/etc/rc.local file to set the mode each
time your system boots. See &man.lptcontrol.8; for more
information.
Checking Printer Communications
Before proceeding to configure the spooling system, you
should make sure the operating system can successfully send
data to your printer. It is a lot easier to debug printer
communication and the spooling system separately.
To test the printer, we will send some text to it. For
printers that can immediately print characters sent to them,
the program &man.lptest.1; is perfect: it generates all 96
printable ASCII characters in 96 lines.
For a PostScript (or other language-based) printer, we
will need a more sophisticated test. A small PostScript
program, such as the following, will suffice:
%!PS
100 100 moveto 300 300 lineto stroke
310 310 moveto /Helvetica findfont 12 scalefont setfont
(Is this thing working?) show
showpage
The above PostScript code can be placed into a file and
used as shown in the examples appearing in the following
sections.
When this document refers to a printer language, it is
assuming a language like PostScript, and not Hewlett
Packard's PCL. Although PCL has great functionality, you
can intermingle plain text with its escape sequences.
PostScript cannot directly print plain text, and that is the
kind of printer language for which we must make special
accommodations.
Checking a Parallel Printer
This section tells you how to check if FreeBSD can
communicate with a printer connected to a parallel
port.
To test a printer on a parallel
port:
Become root with &man.su.1;.
Send data to the printer.
If the printer can print plain text, then use
&man.lptest.1;. Type:
&prompt.root; lptest > /dev/lptN
Where N is the number
of the parallel port, starting from zero.
If the printer understands PostScript or other
printer language, then send a small program to the
printer. Type:
&prompt.root; cat > /dev/lptN
Then, line by line, type the program
carefully as you cannot edit a
line once you have pressed RETURN
or ENTER . When you have finished
entering the program, press
CONTROL+D , or whatever your end
of file key is.
Alternatively, you can put the program in a file
and type:
&prompt.root; cat file > /dev/lptN
Where file is the
name of the file containing the program you want to
send to the printer.
You should see something print. Do not worry if the
text does not look right; we will fix such things
later.
Checking a Serial Printer
This section tells you how to check if FreeBSD can
communicate with a printer on a serial port.
To test a printer on a serial
port:
Become root with &man.su.1;.
Edit the file /etc/remote . Add
the following entry:
printer:dv=/dev/port :br#bps-rate :pa=parity
Where port is the device
entry for the serial port (ttyd0 ,
ttyd1 , etc.),
bps-rate is the
bits-per-second rate at which the printer communicates,
and parity is the parity
required by the printer (either even ,
odd , none , or
zero ).
Here is a sample entry for a printer connected via
a serial line to the third serial port at 19200 bps with
no parity:
printer:dv=/dev/ttyd2:br#19200:pa=none
Connect to the printer with &man.tip.1;.
Type:
&prompt.root; tip printer
If this step does not work, edit the file
/etc/remote again and try using
/dev/cuaaN
instead of
/dev/ttydN .
Send data to the printer.
If the printer can print plain text, then use
&man.lptest.1;. Type:
~ $lptest
If the printer understands PostScript or other
printer language, then send a small program to the
printer. Type the program, line by line,
very carefully as backspacing
or other editing keys may be significant to the
printer. You may also need to type a special
end-of-file key for the printer so it knows it
received the whole program. For PostScript
printers, press CONTROL+D .
Alternatively, you can put the program in a file
and type:
~ >file
Where file is the
name of the file containing the program. After
&man.tip.1; sends the file, press any required
end-of-file key.
You should see something print. Do not worry if the
text does not look right; we will fix that later.
Enabling the Spooler: The /etc/printcap
File
At this point, your printer should be hooked up, your kernel
configured to communicate with it (if necessary), and you have
been able to send some simple data to the printer. Now, we are
ready to configure LPD to control access to your printer.
You configure LPD by editing the file
/etc/printcap . The LPD spooling system
reads this file each time the spooler is used, so updates to the
file take immediate effect.
The format of the &man.printcap.5; file is straightforward.
Use your favorite text editor to make changes to
/etc/printcap . The format is identical to
other capability files like
/usr/share/misc/termcap and
/etc/remote . For complete information
about the format, see the &man.cgetent.3;.
The simple spooler configuration consists of the following
steps:
Pick a name (and a few convenient aliases) for the
printer, and put them in the
/etc/printcap file; see the
Naming the Printer
section for more information on naming.
Turn off header pages (which are on by default) by
inserting the sh capability; see the
Suppressing Header
Pages section for more information.
Make a spooling directory, and specify its location with
the sd capability; see the Making the Spooling
Directory section for more information.
Set the /dev entry to use for the
printer, and note it in /etc/printcap
with the lp capability; see the Identifying the Printer
Device for more information. Also, if the printer is
on a serial port, set up the communication parameters with
the fs , fc ,
xs , and xc
capabilities; which is discussed in the Configuring Spooler
Communications Parameters section.
Install a plain text input filter; see the Installing the Text
Filter section for details.
Test the setup by printing something with the
&man.lpr.1; command. More details are available in the
Trying It Out and
Troubleshooting
sections.
Language-based printers, such as PostScript printers,
cannot directly print plain text. The simple setup outlined
above and described in the following sections assumes that if
you are installing such a printer you will print only files
that the printer can understand.
Users often expect that they can print plain text to any of
the printers installed on your system. Programs that interface
to LPD to do their printing usually make the same assumption.
If you are installing such a printer and want to be able to
print jobs in the printer language and
print plain text jobs, you are strongly urged to add an
additional step to the simple setup outlined above: install an
automatic plain-text-to-PostScript (or other printer language)
conversion program. The section entitled Accommodating Plain
Text Jobs on PostScript Printers tells how to do
this.
Naming the Printer
The first (easy) step is to pick a name for your printer
It really does not matter whether you choose functional or
whimsical names since you can also provide a number of aliases
for the printer.
At least one of the printers specified in the
/etc/printcap should have the alias
lp . This is the default printer's name.
If users do not have the PRINTER environment
variable nor specify a printer name on the command line of any
of the LPD commands, then lp will be the
default printer they get to use.
Also, it is common practice to make the last alias for a
printer be a full description of the printer, including make
and model.
Once you have picked a name and some common aliases, put
them in the /etc/printcap file. The name
of the printer should start in the leftmost column. Separate
each alias with a vertical bar and put a colon after the last
alias.
In the following example, we start with a skeletal
/etc/printcap that defines two printers
(a Diablo 630 line printer and a Panasonic KX-P4455 PostScript
laser printer):
#
# /etc/printcap for host rose
#
rattan|line|diablo|lp|Diablo 630 Line Printer:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:
In this example, the first printer is named
rattan and has as aliases
line , diablo ,
lp , and Diablo 630 Line
Printer . Since it has the alias
lp , it is also the default printer. The
second is named bamboo , and has as aliases
ps , PS ,
S , panasonic , and
Panasonic KX-P4455 PostScript v51.4 .
Making the Spooling Directory
The next step in the simple spooler setup is to make a
spooling directory , a directory where
print jobs reside until they are printed, and where a number
of other spooler support files live.
Because of the variable nature of spooling directories, it
is customary to put these directories under
/var/spool . It is not necessary to
backup the contents of spooling directories, either.
Recreating them is as simple as running &man.mkdir.1;.
It is also customary to make the directory with a name
that is identical to the name of the printer, as shown
below:
&prompt.root; mkdir /var/spool/printer-name
However, if you have a lot of printers on your network,
you might want to put the spooling directories under a single
directory that you reserve just for printing with LPD. We
will do this for our two example printers
rattan and
bamboo :
&prompt.root; mkdir /var/spool/lpd
&prompt.root; mkdir /var/spool/lpd/rattan
&prompt.root; mkdir /var/spool/lpd/bamboo
If you are concerned about the privacy of jobs that
users print, you might want to protect the spooling
directory so it is not publicly accessible. Spooling
directories should be owned and be readable, writable, and
searchable by user daemon and group daemon, and no one else.
We will do this for our example printers:
&prompt.root; chown daemon:daemon /var/spool/lpd/rattan
&prompt.root; chown daemon:daemon /var/spool/lpd/bamboo
&prompt.root; chmod 770 /var/spool/lpd/rattan
&prompt.root; chmod 770 /var/spool/lpd/bamboo
Finally, you need to tell LPD about these directories
using the /etc/printcap file. You
specify the pathname of the spooling directory with the
sd capability:
#
# /etc/printcap for host rose - added spooling directories
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:
Note that the name of the printer starts in the first
column but all other entries describing the printer should be
indented with a tab and each line escaped with a
backslash.
If you do not specify a spooling directory with
sd , the spooling system will use
/var/spool/lpd as a default.
Identifying the Printer Device
In the Adding
/dev Entries for the Ports
section, we identified which entry in the
/dev directory FreeBSD will use to
communicate with the printer. Now, we tell LPD that
information. When the spooling system has a job to print, it
will open the specified device on behalf of the filter program
(which is responsible for passing data to the printer).
List the /dev entry pathname in the
/etc/printcap file using the
lp capability.
In our running example, let us assume that
rattan is on the first parallel port, and
bamboo is on a sixth serial port; here are
the additions to /etc/printcap :
#
# /etc/printcap for host rose - identified what devices to use
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:
If you do not specify the lp capability
for a printer in your /etc/printcap file,
LPD uses /dev/lp as a default.
/dev/lp currently does not exist in
FreeBSD.
If the printer you are installing is connected to a
parallel port, skip to the section entitled, Installing the Text
Filter. Otherwise, be sure to follow the instructions
in the next section.
Configuring Spooler Communication Parameters
For printers on serial ports, LPD can set up the bps rate,
parity, and other serial communication parameters on behalf of
the filter program that sends data to the printer. This is
advantageous since:
It lets you try different communication parameters by
simply editing the /etc/printcap
file; you do not have to recompile the filter
program.
It enables the spooling system to use the same filter
program for multiple printers which may have different
serial communication settings.
The following /etc/printcap
capabilities control serial communication parameters of the
device listed in the lp capability:
br#bps-rate
Sets the communications speed of the device to
bps-rate , where
bps-rate can be 50, 75, 110,
134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600,
19200, or 38400 bits-per-second.
fc#clear-bits
Clears the flag bits
clear-bits in the
sgttyb structure after
opening the device.
fs#set-bits
Sets the flag bits
set-bits in the
sgttyb structure.
xc#clear-bits
Clears local mode bits
clear-bits after opening the
device.
xs#set-bits
Sets local mode bits
set-bits .
For more information on the bits for the
fc , fs ,
xc , and xs capabilities,
see the file
/usr/include/sys/ioctl_compat.h .
When LPD opens the device specified by the
lp capability, it reads the flag bits in
the sgttyb structure; it clears any bits in
the fc capability, then sets bits in the
fs capability, then applies the resultant
setting. It does the same for the local mode bits as
well.
Let us add to our example printer on the sixth serial
port. We will set the bps rate to 38400. For the flag bits,
we will set the TANDEM ,
ANYP , LITOUT ,
FLUSHO , and PASS8 flags.
For the local mode bits, we will set the
LITOUT and PASS8
flags:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000c1:xs#0x820:
Installing the Text Filter
We are now ready to tell LPD what text filter to use to
send jobs to the printer. A text filter ,
also known as an input filter , is a
program that LPD runs when it has a job to print. When LPD
runs the text filter for a printer, it sets the filter's
standard input to the job to print, and its standard output to
the printer device specified with the lp
capability. The filter is expected to read the job from
standard input, perform any necessary translation for the
printer, and write the results to standard output, which will
get printed. For more information on the text filter, see
the Filters
section.
For our simple printer setup, the text filter can be a
small shell script that just executes
/bin/cat to send the job to the printer.
FreeBSD comes with another filter called
lpf that handles backspacing and
underlining for printers that might not deal with such
character streams well. And, of course, you can use any other
filter program you want. The filter lpf is
described in detail in section entitled lpf: a Text
Filter.
First, let us make the shell script
/usr/local/libexec/if-simple be a simple
text filter. Put the following text into that file with your
favorite text editor:
#!/bin/sh
#
# if-simple - Simple text input filter for lpd
# Installed in /usr/local/libexec/if-simple
#
# Simply copies stdin to stdout. Ignores all filter arguments.
/bin/cat && exit 0
exit 2
Make the file executable:
&prompt.root; chmod 555 /usr/local/libexec/if-simple
And then tell LPD to use it by specifying it with the
if capability in
/etc/printcap . We will add it to the two
printers we have so far in the example
/etc/printcap :
#
# /etc/printcap for host rose - added text filter
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\ :lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:\
:if=/usr/local/libexec/if-simple:
Turn on LPD
&man.lpd.8; is run from /etc/rc ,
controlled by the lpd_enable variable. This
variable defaults to NO . If you have not done
so already, add the line:
lpd_enable="YES"
to /etc/rc.conf , and then either restart
your machine, or just run &man.lpd.8;.
&prompt.root; lpd
Trying It Out
You have reached the end of the simple LPD setup.
Unfortunately, congratulations are not quite yet in order,
since we still have to test the setup and correct any
problems. To test the setup, try printing something. To
print with the LPD system, you use the command &man.lpr.1;,
which submits a job for printing.
You can combine &man.lpr.1; with the &man.lptest.1;
program, introduced in section Checking Printer
Communications to generate some test text.
To test the simple LPD setup:
Type:
&prompt.root; lptest 20 5 | lpr -Pprinter-name
Where printer-name is a the
name of a printer (or an alias) specified in
/etc/printcap . To test the default
printer, type &man.lpr.1; without any -P
argument. Again, if you are testing a printer that expects
PostScript, send a PostScript program in that language instead
of using &man.lptest.1;. You can do so by putting the program
in a file and typing lpr
file .
For a PostScript printer, you should get the results of
the program. If you are using &man.lptest.1;, then your
results should look like the following:
!"#$%&'()*+,-./01234
"#$%&'()*+,-./012345
#$%&'()*+,-./0123456
$%&'()*+,-./01234567
%&'()*+,-./012345678
To further test the printer, try downloading larger
programs (for language-based printers) or running
&man.lptest.1; with different arguments. For example,
lptest 80 60 will produce 60 lines of 80
characters each.
If the printer did not work, see the Troubleshooting
section.
Advanced Printer Setup
This section describes filters for printing specially formatted
files, header pages, printing across networks, and restricting and
accounting for printer usage.
Filters
Although LPD handles network protocols, queuing, access control,
and other aspects of printing, most of the real
work happens in the filters . Filters are
programs that communicate with the printer and handle its device
dependencies and special requirements. In the simple printer setup,
we installed a plain text filter—an extremely simple one that
should work with most printers (section Installing the Text
Filter).
However, in order to take advantage of format conversion, printer
accounting, specific printer quirks, and so on, you should understand
how filters work. It will ultimately be the filter's responsibility
to handle these aspects. And the bad news is that most of the time
you have to provide filters yourself. The good
news is that many are generally available; when they are not, they are
usually easy to write.
Also, FreeBSD comes with one,
/usr/libexec/lpr/lpf , that works with many
printers that can print plain text. (It handles backspacing and tabs
in the file, and does accounting, but that is about all it does.)
There are also several filters and filter components in the FreeBSD
ports collection.
Here is what you will find in this section:
Section How Filters
Work, tries to give an overview of a filter's role in the
printing process. You should read this section to get an
understanding of what is happening under the hood
when LPD uses filters. This knowledge could help you anticipate
and debug problems you might encounter as you install more and
more filters on each of your printers.
LPD expects every printer to be able to print plain text by
default. This presents a problem for PostScript (or other
language-based printers) which cannot directly print plain text.
Section Accommodating
Plain Text Jobs on PostScript Printers tells you what you
should do to overcome this problem. You should read this
section if you have a PostScript printer.
PostScript is a popular output format for many programs. Even
some people (myself included) write PostScript code directly. But
PostScript printers are expensive. Section Simulating PostScript on
Non-PostScript Printers tells how you can further modify
a printer's text filter to accept and print PostScript data on a
non-PostScript printer. You should read
this section if you do not have a PostScript printer.
Section Conversion
Filters tells about a way you can automate the conversion
of specific file formats, such as graphic or typesetting data,
into formats your printer can understand. After reading this
section, you should be able to set up your printers such that
users can type lpr -t to print troff data, or
lpr -d to print TeX DVI data, or lpr
-v to print raster image data, and so forth. I
recommend reading this section.
Section Output
Filters tells all about a not often used feature of LPD:
output filters. Unless you are printing header pages (see Header Pages),
you can probably skip that section altogether.
Section lpf: a Text
Filter describes lpf , a fairly
complete if simple text filter for line printers (and laser
printers that act like line printers) that comes with FreeBSD. If
you need a quick way to get printer accounting working for plain
text, or if you have a printer which emits smoke when it sees
backspace characters, you should definitely consider
lpf .
How Filters Work
As mentioned before, a filter is an executable program started
by LPD to handle the device-dependent part of communicating with the
printer.
When LPD wants to print a file in a job, it starts a filter
program. It sets the filter's standard input to the file to print,
its standard output to the printer, and its standard error to the
error logging file (specified in the lf
capability in /etc/printcap , or
/dev/console by default).
Which filter LPD starts and the filter's arguments depend on
what is listed in the /etc/printcap file and
what arguments the user specified for the job on the
&man.lpr.1; command line. For example, if the user typed
lpr -t , LPD would start the troff filter, listed
in the tf capability for the destination printer.
If the user wanted to print plain text, it would start the
if filter (this is mostly true: see Output Filters for
details).
There are three kinds of filters you can specify in
/etc/printcap :
The text filter , confusingly called the
input filter in LPD documentation, handles
regular text printing. Think of it as the default filter. LPD
expects every printer to be able to print plain text by default,
and it is the text filter's job to make sure backspaces, tabs,
or other special characters do not confuse the printer. If you
are in an environment where you have to account for printer
usage, the text filter must also account for pages printed,
usually by counting the number of lines printed and comparing
that to the number of lines per page the printer supports. The
text filter is started with the following argument list:
filter-name
-c
-wwidth
-llength
-iindent
-n login
-h host
acct-file
where
-c
appears if the job's submitted with lpr
-l
width
is the value from the pw (page
width) capability specified in
/etc/printcap , default 132
length
is the value from the pl (page
length) capability, default 66
indent
is the amount of the indentation from lpr
-i , default 0
login
is the account name of the user printing the
file
host
is the host name from which the job was
submitted
acct-file
is the name of the accounting file from the
af capability.
A conversion filter converts a specific
file format into one the printer can render onto paper. For
example, ditroff typesetting data cannot be directly printed,
but you can install a conversion filter for ditroff files to
convert the ditroff data into a form the printer can digest and
print. Section Conversion
Filters tells all about them. Conversion filters also
need to do accounting, if you need printer accounting.
Conversion filters are started with the following arguments:
filter-name
-xpixel-width
-ypixel-height
-n login
-h host
acct-file
where pixel-width is the value
from the px capability (default 0) and
pixel-height is the value from the
py capability (default 0).
The output filter is used only if there
is no text filter, or if header pages are enabled. In my
experience, output filters are rarely used. Section Output Filters describe
them. There are only two arguments to an output filter:
filter-name
-wwidth
-llength
which are identical to the text filters -w and
-l arguments.
Filters should also exit with the
following exit status:
exit 0
If the filter printed the file successfully.
exit 1
If the filter failed to print the file but wants LPD to
try to print the file again. LPD will restart a filter if it
exits with this status.
exit 2
If the filter failed to print the file and does not want
LPD to try again. LPD will throw out the file.
The text filter that comes with the FreeBSD release,
/usr/libexec/lpr/lpf , takes advantage of the
page width and length arguments to determine when to send a form
feed and how to account for printer usage. It uses the login, host,
and accounting file arguments to make the accounting entries.
If you are shopping for filters, see if they are LPD-compatible.
If they are, they must support the argument lists described above.
If you plan on writing filters for general use, then have them
support the same argument lists and exit codes.
Accommodating Plain Text Jobs on PostScript Printers
If you are the only user of your computer and PostScript (or
other language-based) printer, and you promise to never send plain
text to your printer and to never use features of various programs
that will want to send plain text to your printer, then you do not
need to worry about this section at all.
But, if you would like to send both PostScript and plain text
jobs to the printer, then you are urged to augment your printer
setup. To do so, we have the text filter detect if the arriving job
is plain text or PostScript. All PostScript jobs must start with
%! (for other printer languages, see your printer
documentation). If those are the first two characters in the job,
we have PostScript, and can pass the rest of the job directly. If
those are not the first two characters in the file, then the filter
will convert the text into PostScript and print the result.
How do we do this?
If you have got a serial printer, a great way to do it is to
install lprps . lprps is a
PostScript printer filter which performs two-way communication with
the printer. It updates the printer's status file with verbose
information from the printer, so users and administrators can see
exactly what the state of the printer is (such as toner
low or paper jam ). But more
importantly, it includes a program called psif
which detects whether the incoming job is plain text and calls
textps (another program that comes with
lprps ) to convert it to PostScript. It then uses
lprps to send the job to the printer.
lprps is part of the FreeBSD ports collection
(see The Ports Collection). You can
fetch, build and install it yourself, of course. After installing
lprps , just specify the pathname to the
psif program that is part of
lprps . If you installed lprps
from the ports collection, use the following in the serial
PostScript printer's entry in
/etc/printcap :
:if=/usr/local/libexec/psif:
You should also specify the rw capability;
that tells LPD to open the printer in read-write mode.
If you have a parallel PostScript printer (and therefore cannot
use two-way communication with the printer, which
lprps needs), you can use the following shell
script as the text filter:
#!/bin/sh
#
# psif - Print PostScript or plain text on a PostScript printer
# Script version; NOT the version that comes with lprps
# Installed in /usr/local/libexec/psif
#
read first_line
first_two_chars=`expr "$first_line" : '\(..\)'`
if [ "$first_two_chars" = "%!" ]; then
#
# PostScript job, print it.
#
echo "$first_line" && cat && printf "\004" && exit 0
exit 2
else
#
# Plain text, convert it, then print it.
#
( echo "$first_line"; cat ) | /usr/local/bin/textps && printf "\004" && exit 0
exit 2
fi
In the above script, textps is a program we
installed separately to convert plain text to PostScript. You can
use any text-to-PostScript program you wish. The FreeBSD ports
collection (see The Ports Collection)
includes a full featured text-to-PostScript program called
a2ps that you might want to investigate.
Simulating PostScript on Non-PostScript Printers
PostScript is the de facto standard for
high quality typesetting and printing. PostScript is, however, an
expensive standard. Thankfully, Alladin
Enterprises has a free PostScript work-alike called
Ghostscript that runs with FreeBSD.
Ghostscript can read most PostScript files and can render their
pages onto a variety of devices, including many brands of
non-PostScript printers. By installing Ghostscript and using a
special text filter for your printer, you can make your
non-PostScript printer act like a real PostScript printer.
Ghostscript is in the FreeBSD ports collection, if you
would like to install it from there. You can fetch, build, and
install it quite easily yourself, as well.
To simulate PostScript, we have the text filter detect if it is
printing a PostScript file. If it is not, then the filter will pass
the file directly to the printer; otherwise, it will use Ghostscript
to first convert the file into a format the printer will
understand.
Here is an example: the following script is a text filter
for Hewlett Packard DeskJet 500 printers. For other printers,
substitute the -sDEVICE argument to the
gs (Ghostscript) command. (Type gs
-h to get a list of devices the current installation of
Ghostscript supports.)
#!/bin/sh
#
# ifhp - Print Ghostscript-simulated PostScript on a DeskJet 500
# Installed in /usr/local/libexec/hpif
#
# Treat LF as CR+LF:
#
printf "\033&k2G" || exit 2
#
# Read first two characters of the file
#
read first_line
first_two_chars=`expr "$first_line" : '\(..\)'`
if [ "$first_two_chars" = "%!" ]; then
#
# It is PostScript; use Ghostscript to scan-convert and print it.
#
# Note that PostScript files are actually interpreted programs,
# and those programs are allowed to write to stdout, which will
# mess up the printed output. So, we redirect stdout to stderr
# and then make descriptor 3 go to stdout, and have Ghostscript
# write its output there. Exercise for the clever reader:
# capture the stderr output from Ghostscript and mail it back to
# the user originating the print job.
#
exec 3>&1 1>&2
/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 \
-sOutputFile=/dev/fd/3 - && exit 0
#
/usr/local/bin/gs -dSAFER -dNOPAUSE -q -sDEVICE=djet500 -sOutputFile=- - \
&& exit 0
else
#
# Plain text or HP/PCL, so just print it directly; print a form
# at the end to eject the last page.
#
echo $first_line && cat && printf "\033&l0H" &&
exit 0
fi
exit 2
Finally, you need to notify LPD of the filter via the
if capability:
:if=/usr/local/libexec/hpif:
That is it. You can type lpr plain.text and
lpr whatever.ps and both should print
successfully.
Conversion Filters
After completing the simple setup described in Simple Printer Setup, the first
thing you will probably want to do is install conversion filters for
your favorite file formats (besides plain ASCII text).
Why Install Conversion Filters?
Conversion filters make printing various kinds of files easy.
As an example, suppose we do a lot of work with the TeX
typesetting system, and we have a PostScript printer. Every time
we generate a DVI file from TeX, we cannot print it directly until
we convert the DVI file into PostScript. The command sequence
goes like this:
&prompt.user; dvips seaweed-analysis.dvi
&prompt.user; lpr seaweed-analysis.ps
By installing a conversion filter for DVI files, we can skip
the hand conversion step each time by having LPD do it for us.
Now, each time we get a DVI file, we are just one step away from
printing it:
&prompt.user; lpr -d seaweed-analysis.dvi
We got LPD to do the DVI file conversion for us by specifying
the -d option. Section Formatting and Conversion
Options lists the conversion options.
For each of the conversion options you want a printer to
support, install a conversion filter and
specify its pathname in /etc/printcap . A
conversion filter is like the text filter for the simple printer
setup (see section Installing
the Text Filter) except that instead of printing plain
text, the filter converts the file into a format the printer can
understand.
Which Conversions Filters Should I Install?
You should install the conversion filters you expect to use.
If you print a lot of DVI data, then a DVI conversion filter is in
order. If you have got plenty of troff to print out, then you
probably want a troff filter.
The following table summarizes the filters that LPD works
with, their capability entries for the
/etc/printcap file, and how to invoke them
with the lpr command:
File type
/etc/printcap capability
lpr option
cifplot
cf
-c
DVI
df
-d
plot
gf
-g
ditroff
nf
-n
FORTRAN text
rf
-f
troff
rf
-f
raster
vf
-v
plain text
if
none, -p , or
-l
In our example, using lpr -d means the
printer needs a df capability in its entry in
/etc/printcap .
Despite what others might contend, formats like FORTRAN text
and plot are probably obsolete. At your site, you can give new
meanings to these or any of the formatting options just by
installing custom filters. For example, suppose you would like to
directly print Printerleaf files (files from the Interleaf desktop
publishing program), but will never print plot files. You could
install a Printerleaf conversion filter under the
gf capability and then educate your users that
lpr -g mean print Printerleaf
files.
Installing Conversion Filters
Since conversion filters are programs you install outside of
the base FreeBSD installation, they should probably go under
/usr/local . The directory
/usr/local/libexec is a popular location,
since they are specialized programs that only LPD will run;
regular users should not ever need to run them.
To enable a conversion filter, specify its pathname under the
appropriate capability for the destination printer in
/etc/printcap .
In our example, we will add the DVI conversion filter to the
entry for the printer named bamboo . Here is
the example /etc/printcap file again, with
the new df capability for the printer
bamboo .
#
# /etc/printcap for host rose - added df filter for bamboo
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:
The DVI filter is a shell script named
/usr/local/libexec/psdf . Here is that
script:
#!bin/sh
#
# psdf - DVI to PostScript printer filter
# Installed in /usr/local/libexec/psdf
#
# Invoked by lpd when user runs lpr -d
#
exec /usr/local/bin/dvips -f | /usr/local/libexec/lprps "$@"
This script runs dvips in filter mode (the
-f argument) on standard input, which is the job
to print. It then starts the PostScript printer filter
lprps (see section Accommodating Plain
Text Jobs on PostScript Printers) with the arguments LPD
passed to this script. lprps will use those
arguments to account for the pages printed.
More Conversion Filter Examples
Since there is no fixed set of steps to install conversion
filters, let me instead provide more examples. Use these as
guidance to making your own filters. Use them directly, if
appropriate.
This example script is a raster (well, GIF file, actually)
conversion filter for a Hewlett Packard LaserJet III-Si
printer:
#!/bin/sh
#
# hpvf - Convert GIF files into HP/PCL, then print
# Installed in /usr/local/libexec/hpvf
PATH=/usr/X11R6/bin:$PATH; export PATH
giftopnm | ppmtopgm | pgmtopbm | pbmtolj -resolution 300 \
&& exit 0 \
|| exit 2
It works by converting the GIF file into a portable anymap,
converting that into a portable graymap, converting that into a
portable bitmap, and converting that into LaserJet/PCL-compatible
data.
Here is the /etc/printcap file with an
entry for a printer using the above filter:
#
# /etc/printcap for host orchid
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sh:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/hpif:\
:vf=/usr/local/libexec/hpvf:
The following script is a conversion filter for troff data
from the groff typesetting system for the PostScript printer named
bamboo :
#!/bin/sh
#
# pstf - Convert groff's troff data into PS, then print.
# Installed in /usr/local/libexec/pstf
#
exec grops | /usr/local/libexec/lprps "$@"
The above script makes use of lprps again
to handle the communication with the printer. If the printer were
on a parallel port, we would use this script instead:
#!/bin/sh
#
# pstf - Convert groff's troff data into PS, then print.
# Installed in /usr/local/libexec/pstf
#
exec grops
That is it. Here is the entry we need to add to
/etc/printcap to enable the filter:
:tf=/usr/local/libexec/pstf:
Here is an example that might make old hands at FORTRAN blush.
It is a FORTRAN-text filter for any printer that can directly
print plain text. We will install it for the printer
teak :
#!/bin/sh
#
# hprf - FORTRAN text filter for LaserJet 3si:
# Installed in /usr/local/libexec/hprf
#
printf "\033&k2G" && fpr && printf "\033&l0H" &&
exit 0
exit 2
And we will add this line to the
/etc/printcap for the printer
teak to enable this filter:
:rf=/usr/local/libexec/hprf:
Here is one final, somewhat complex example. We will add a
DVI filter to the LaserJet printer teak
introduced earlier. First, the easy part: updating
/etc/printcap with the location of the DVI
filter:
:df=/usr/local/libexec/hpdf:
Now, for the hard part: making the filter. For that, we need
a DVI-to-LaserJet/PCL conversion program. The FreeBSD ports
collection (see The Ports Collection)
has one: dvi2xx is the name of the package.
Installing this package gives us the program we need,
dvilj2p , which converts DVI into LaserJet IIp,
LaserJet III, and LaserJet 2000 compatible codes.
dvilj2p makes the filter
hpdf quite complex since
dvilj2p cannot read from standard input. It
wants to work with a filename. What is worse, the filename has to
end in .dvi so using
/dev/fd/0 for standard input is problematic.
We can get around that problem by linking (symbolically) a
temporary file name (one that ends in .dvi )
to /dev/fd/0 , thereby forcing
dvilj2p to read from standard input.
The only other fly in the ointment is the fact that we cannot
use /tmp for the temporary link. Symbolic
links are owned by user and group bin . The
filter runs as user daemon . And the
/tmp directory has the sticky bit set. The
filter can create the link, but it will not be able clean up when
done and remove it since the link will belong to a different
user.
Instead, the filter will make the symbolic link in the current
working directory, which is the spooling directory (specified by
the sd capability in
/etc/printcap ). This is a perfect place for
filters to do their work, especially since there is (sometimes)
more free disk space in the spooling directory than under
/tmp .
Here, finally, is the filter:
#!/bin/sh
#
# hpdf - Print DVI data on HP/PCL printer
# Installed in /usr/local/libexec/hpdf
PATH=/usr/local/bin:$PATH; export PATH
#
# Define a function to clean up our temporary files. These exist
# in the current directory, which will be the spooling directory
# for the printer.
#
cleanup() {
rm -f hpdf$$.dvi
}
#
# Define a function to handle fatal errors: print the given message
# and exit 2. Exiting with 2 tells LPD to do not try to reprint the
# job.
#
fatal() {
echo "$@" 1>&2
cleanup
exit 2
}
#
# If user removes the job, LPD will send SIGINT, so trap SIGINT
# (and a few other signals) to clean up after ourselves.
#
trap cleanup 1 2 15
#
# Make sure we are not colliding with any existing files.
#
cleanup
#
# Link the DVI input file to standard input (the file to print).
#
ln -s /dev/fd/0 hpdf$$.dvi || fatal "Cannot symlink /dev/fd/0"
#
# Make LF = CR+LF
#
printf "\033&k2G" || fatal "Cannot initialize printer"
#
# Convert and print. Return value from dvilj2p does not seem to be
# reliable, so we ignore it.
#
dvilj2p -M1 -q -e- dfhp$$.dvi
#
# Clean up and exit
#
cleanup
exit 0
Automated Conversion: An Alternative To Conversion
Filters
All these conversion filters accomplish a lot for your
printing environment, but at the cost forcing the user to specify
(on the &man.lpr.1; command line) which one to use.
If your users are not particularly computer literate, having to
specify a filter option will become annoying. What is worse,
though, is that an incorrectly specified filter option may run a
filter on the wrong type of file and cause your printer to spew
out hundreds of sheets of paper.
Rather than install conversion filters at all, you might want
to try having the text filter (since it is the default filter)
detect the type of file it has been asked to print and then
automatically run the right conversion filter. Tools such as
file can be of help here. Of course, it will
be hard to determine the differences between
some file types—and, of course, you can
still provide conversion filters just for them.
The FreeBSD ports collection has a text filter that performs
automatic conversion called apsfilter . It can
detect plain text, PostScript, and DVI files, run the proper
conversions, and print.
Output Filters
The LPD spooling system supports one other type of filter that
we have not yet explored: an output filter. An output filter is
intended for printing plain text only, like the text filter, but
with many simplifications. If you are using an output filter but no
text filter, then:
LPD starts an output filter once for the entire job instead
of once for each file in the job.
LPD does not make any provision to identify the start or the
end of files within the job for the output filter.
LPD does not pass the user's login or host to the filter, so
it is not intended to do accounting. In fact, it gets only two
arguments:
filter-name
-wwidth
-llength
Where width is from the
pw capability and
length is from the
pl capability for the printer in
question.
Do not be seduced by an output filter's simplicity. If you
would like each file in a job to start on a different page an output
filter will not work . Use a text filter (also
known as an input filter); see section Installing the Text Filter.
Furthermore, an output filter is actually more
complex in that it has to examine the byte stream being
sent to it for special flag characters and must send signals to
itself on behalf of LPD.
However, an output filter is necessary if
you want header pages and need to send escape sequences or other
initialization strings to be able to print the header page. (But it
is also futile if you want to charge header
pages to the requesting user's account, since LPD does not give any
user or host information to the output filter.)
On a single printer, LPD allows both an output filter and text
or other filters. In such cases, LPD will start the output filter
to print the header page (see section Header Pages)
only. LPD then expects the output filter to stop
itself by sending two bytes to the filter: ASCII 031
followed by ASCII 001. When an output filter sees these two bytes
(031, 001), it should stop by sending SIGSTOP to itself. When LPD's
done running other filters, it will restart the output filter by
sending SIGCONT to it.
If there is an output filter but no text
filter and LPD is working on a plain text job, LPD uses the output
filter to do the job. As stated before, the output filter will
print each file of the job in sequence with no intervening form
feeds or other paper advancement, and this is probably
not what you want. In almost all cases, you
need a text filter.
The program lpf , which we introduced earlier
as a text filter, can also run as an output filter. If you need a
quick-and-dirty output filter but do not want to write the byte
detection and signal sending code, try lpf . You
can also wrap lpf in a shell script to handle any
initialization codes the printer might require.
lpf : a Text Filter
The program /usr/libexec/lpr/lpf that comes
with FreeBSD binary distribution is a text filter (input filter)
that can indent output (job submitted with lpr
-i ), allow literal characters to pass (job submitted
with lpr -l ), adjust the printing position for
backspaces and tabs in the job, and account for pages printed. It
can also act like an output filter.
lpf is suitable for many printing
environments. And although it has no capability to send
initialization sequences to a printer, it is easy to write a shell
script to do the needed initialization and then execute
lpf .
In order for lpf to do page accounting
correctly, it needs correct values filled in for the
pw and pl capabilities in the
/etc/printcap file. It uses these values to
determine how much text can fit on a page and how many pages were in
a user's job. For more information on printer accounting, see Accounting for Printer
Usage.
Networked Printing
FreeBSD supports networked printing: sending jobs to remote
printers. Networked printing generally refers to two different
things:
Accessing a printer attached to a remote host. You install a
printer that has a conventional serial or parallel interface on
one host. Then, you set up LPD to enable access to the printer
from other hosts on the network. Section Printers Installed on
Remote Hosts tells how to do this.
Accessing a printer attached directly to a network. The
printer has a network interface in addition (or in place of) a
more conventional serial or parallel interface. Such a printer
might work as follows:
It might understand the LPD protocol and can even queue
jobs from remote hosts. In this case, it acts just like a
regular host running LPD. Follow the same procedure in
section Printers
Installed on Remote Hosts to set up such a
printer.
It might support a data stream network connection. In this
case, you attach
the printer to one host on the
network by making that host responsible for spooling jobs and
sending them to the printer. Section Printers with
Networked Data Stream Interfaces gives some
suggestions on installing such printers.
Printers Installed on Remote Hosts
The LPD spooling system has built-in support for sending jobs to
other hosts also running LPD (or are compatible with LPD). This
feature enables you to install a printer on one host and make it
accessible from other hosts. It also works with printers that have
network interfaces that understand the LPD protocol.
To enable this kind of remote printing, first install a printer
on one host, the printer host , using the simple
printer setup described in Simple
Printer Setup. Do any advanced setup in Advanced Printer Setup that you
need. Make sure to test the printer and see if it works with the
features of LPD you have enabled. Also ensure that the
local host has authorization to use the LPD
service in the remote host (see Restricting Jobs
from Remote Printers).
If you are using a printer with a network interface that is
compatible with LPD, then the printer host in
the discussion below is the printer itself, and the
printer name is the name you configured for the
printer. See the documentation that accompanied your printer and/or
printer-network interface.
If you are using a Hewlett Packard Laserjet then the printer
name text will automatically perform the LF to
CRLF conversion for you, so you will not require the
hpif script.
Then, on the other hosts you want to have access to the printer,
make an entry in their /etc/printcap files with
the following:
Name the entry anything you want. For simplicity, though,
you probably want to use the same name and aliases as on the
printer host.
Leave the lp capability blank, explicitly
(:lp=: ).
Make a spooling directory and specify its location in the
sd capability. LPD will store jobs here
before they get sent to the printer host.
Place the name of the printer host in the
rm capability.
Place the printer name on the printer
host in the rp
capability.
That is it. You do not need to list conversion filters, page
dimensions, or anything else in the
/etc/printcap file.
Here is an example. The host rose has two
printers, bamboo and rattan .
We will enable users on the host orchid to print to those printers.
Here is the /etc/printcap file for
orchid (back from section Enabling Header
Pages). It already had the entry for the printer
teak ; we have added entries for the two printers
on the host rose:
#
# /etc/printcap for host orchid - added (remote) printers on rose
#
#
# teak is local; it is connected directly to orchid:
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/ifhp:\
:vf=/usr/local/libexec/vfhp:\
:of=/usr/local/libexec/ofhp:
#
# rattan is connected to rose; send jobs for rattan to rose:
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:lp=:rm=rose:rp=rattan:sd=/var/spool/lpd/rattan:
#
# bamboo is connected to rose as well:
#
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:lp=:rm=rose:rp=bamboo:sd=/var/spool/lpd/bamboo:
Then, we just need to make spooling directories on
orchid :
&prompt.root; mkdir -p /var/spool/lpd/rattan /var/spool/lpd/bamboo
&prompt.root; chmod 770 /var/spool/lpd/rattan /var/spool/lpd/bamboo
&prompt.root; chown daemon:daemon /var/spool/lpd/rattan /var/spool/lpd/bamboo
Now, users on orchid can print to
rattan and bamboo . If, for
example, a user on orchid typed
&prompt.user; lpr -P bamboo -d sushi-review.dvi
the LPD system on orchid would copy the job to the spooling
directory /var/spool/lpd/bamboo and note that
it was a DVI job. As soon as the host rose has room in its
bamboo spooling directory, the two LPDs would
transfer the file to rose. The file would wait in rose's queue
until it was finally printed. It would be converted from DVI to
PostScript (since bamboo is a PostScript printer) on rose.
Printers with Networked Data Stream Interfaces
Often, when you buy a network interface card for a printer, you
can get two versions: one which emulates a spooler (the more
expensive version), or one which just lets you send data to it as if
you were using a serial or parallel port (the cheaper version).
This section tells how to use the cheaper version. For the more
expensive one, see the previous section Printers Installed on
Remote Hosts.
The format of the /etc/printcap file lets
you specify what serial or parallel interface to use, and (if you
are using a serial interface), what baud rate, whether to use flow
control, delays for tabs, conversion of newlines, and more. But
there is no way to specify a connection to a printer that is
listening on a TCP/IP or other network port.
To send data to a networked printer, you need to develop a
communications program that can be called by the text and conversion
filters. Here is one such example: the script
netprint takes all data on standard input and
sends it to a network-attached printer. We specify the hostname of
the printer as the first argument and the port number to which to
connect as the second argument to netprint . Note
that this supports one-way communication only (FreeBSD to printer);
many network printers support two-way communication, and you might
want to take advantage of that (to get printer status, perform
accounting, etc.).
#!/usr/bin/perl
#
# netprint - Text filter for printer attached to network
# Installed in /usr/local/libexec/netprint
#
$#ARGV eq 1 || die "Usage: $0 <printer-hostname> <port-number>";
$printer_host = $ARGV[0];
$printer_port = $ARGV[1];
require 'sys/socket.ph';
($ignore, $ignore, $protocol) = getprotobyname('tcp');
($ignore, $ignore, $ignore, $ignore, $address)
= gethostbyname($printer_host);
$sockaddr = pack('S n a4 x8', &AF_INET, $printer_port, $address);
socket(PRINTER, &PF_INET, &SOCK_STREAM, $protocol)
|| die "Can't create TCP/IP stream socket: $!";
connect(PRINTER, $sockaddr) || die "Can't contact $printer_host: $!";
while (<STDIN>) { print PRINTER; }
exit 0;
We can then use this script in various filters. Suppose we had
a Diablo 750-N line printer connected to the network. The printer
accepts data to print on port number 5100. The host name of the
printer is scrivener. Here is the text filter for the
printer:
#!/bin/sh
#
# diablo-if-net - Text filter for Diablo printer `scrivener' listening
# on port 5100. Installed in /usr/local/libexec/diablo-if-net
#
exec /usr/libexec/lpr/lpf "$@" | /usr/local/libexec/netprint scrivener 5100
Restricting Printer Usage
This section gives information on restricting printer usage. The
LPD system lets you control who can access a printer, both locally or
remotely, whether they can print multiple copies, how large their jobs
can be, and how large the printer queues can get.
Restricting Multiple Copies
The LPD system makes it easy for users to print multiple copies
of a file. Users can print jobs with lpr -#5
(for example) and get five copies of each file in the job. Whether
this is a good thing is up to you.
If you feel multiple copies cause unnecessary wear and tear on
your printers, you can disable the -# option to
&man.lpr.1; by adding the sc capability to the
/etc/printcap file. When users submit jobs
with the -# option, they will see:
lpr: multiple copies are not allowed
Note that if you have set up access to a printer remotely (see
section Printers
Installed on Remote Hosts), you need the
sc capability on the remote
/etc/printcap files as well, or else users will
still be able to submit multiple-copy jobs by using another
host.
Here is an example. This is the
/etc/printcap file for the host
rose . The printer rattan is
quite hearty, so we will allow multiple copies, but the laser
printer bamboo 's a bit more delicate, so we will
disable multiple copies by adding the sc
capability:
#
# /etc/printcap for host rose - restrict multiple copies on bamboo
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:
Now, we also need to add the sc capability on
the host orchid 's
/etc/printcap (and while we are at it, let us
disable multiple copies for the printer
teak ):
#
# /etc/printcap for host orchid - no multiple copies for local
# printer teak or remote printer bamboo
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sd=/var/spool/lpd/teak:mx#0:sc:\
:if=/usr/local/libexec/ifhp:\
:vf=/usr/local/libexec/vfhp:\
:of=/usr/local/libexec/ofhp:
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:lp=:rm=rose:rp=rattan:sd=/var/spool/lpd/rattan:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:lp=:rm=rose:rp=bamboo:sd=/var/spool/lpd/bamboo:sc:
By using the sc capability, we prevent the
use of lpr -# , but that still does not prevent
users from running &man.lpr.1;
multiple times, or from submitting the same file multiple times in
one job like this:
&prompt.user; lpr forsale.sign forsale.sign forsale.sign forsale.sign forsale.sign
There are many ways to prevent this abuse (including ignoring
it) which you are free to explore.
Restricting Access To Printers
You can control who can print to what printers by using the UNIX
group mechanism and the rg capability in
/etc/printcap . Just place the users you want
to have access to a printer in a certain group, and then name that
group in the rg capability.
Users outside the group (including root) will be greeted with
lpr: Not a member of the restricted group
if they try to print to the controlled printer.
As with the sc (suppress multiple copies)
capability, you need to specify rg on remote
hosts that also have access to your printers, if you feel it is
appropriate (see section Printers Installed on
Remote Hosts).
For example, we will let anyone access the printer
rattan , but only those in group
artists can use bamboo . Here
is the familiar /etc/printcap for host
rose :
#
# /etc/printcap for host rose - restricted group for bamboo
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:
Let us leave the other example
/etc/printcap file (for the host
orchid ) alone. Of course, anyone on
orchid can print to bamboo . It
might be the case that we only allow certain logins on
orchid anyway, and want them to have access to the
printer. Or not.
There can be only one restricted group per printer.
Controlling Sizes of Jobs Submitted
If you have many users accessing the printers, you probably need
to put an upper limit on the sizes of the files users can submit to
print. After all, there is only so much free space on the
filesystem that houses the spooling directories, and you also need
to make sure there is room for the jobs of other users.
LPD enables you to limit the maximum byte size a file in a job
can be with the mx capability. The units are in
BUFSIZ blocks, which are 1024 bytes. If you put a zero for this
capability, there will be no limit on file size; however, if no
mx capability is specified, then a default limit
of 1000 blocks will be used.
The limit applies to files in a job, and
not the total job size.
LPD will not refuse a file that is larger than the limit you
place on a printer. Instead, it will queue as much of the file up
to the limit, which will then get printed. The rest will be
discarded. Whether this is correct behavior is up for
debate.
Let us add limits to our example printers
rattan and bamboo . Since
those artists' PostScript files tend to be large, we will limit them
to five megabytes. We will put no limit on the plain text line
printer:
#
# /etc/printcap for host rose
#
#
# No limit on job size:
#
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:mx#0:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:
#
# Limit of five megabytes:
#
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:mx#5000:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:
Again, the limits apply to the local users only. If you have
set up access to your printers remotely, remote users will not get
those limits. You will need to specify the mx
capability in the remote /etc/printcap files as
well. See section Printers Installed on
Remote Hosts for more information on remote
printing.
There is another specialized way to limit job sizes from remote
printers; see section Restricting Jobs
from Remote Printers.
Restricting Jobs from Remote Printers
The LPD spooling system provides several ways to restrict print
jobs submitted from remote hosts:
Host restrictions
You can control from which remote hosts a local LPD
accepts requests with the files
/etc/hosts.equiv and
/etc/hosts.lpd . LPD checks to see if an
incoming request is from a host listed in either one of these
files. If not, LPD refuses the request.
The format of these files is simple: one host name per
line. Note that the file
/etc/hosts.equiv is also used by the
&man.ruserok.3; protocol, and affects programs like
&man.rsh.1; and &man.rcp.1;, so be careful.
For example, here is the
/etc/hosts.lpd file on the host
rose :
orchid
violet
madrigal.fishbaum.de
This means rose will accept requests from
the hosts orchid , violet ,
and madrigal.fishbaum.de . If any
other host tries to access rose 's
LPD, the job will be refused.
Size restrictions
You can control how much free space there needs to remain
on the filesystem where a spooling directory resides. Make a
file called minfree in the spooling
directory for the local printer. Insert in that file a number
representing how many disk blocks (512 bytes) of free space
there has to be for a remote job to be accepted.
This lets you insure that remote users will not fill your
filesystem. You can also use it to give a certain priority to
local users: they will be able to queue jobs long after the
free disk space has fallen below the amount specified in the
minfree file.
For example, let us add a minfree
file for the printer bamboo . We examine
/etc/printcap to find the spooling
directory for this printer; here is bamboo 's
entry:
bamboo|ps|PS|S|panasonic|Panasonic KX-P4455 PostScript v51.4:\
:sh:sd=/var/spool/lpd/bamboo:sc:rg=artists:mx#5000:\
:lp=/dev/ttyd5:fs#0x82000e1:xs#0x820:rw:mx#5000:\
:if=/usr/local/libexec/psif:\
:df=/usr/local/libexec/psdf:
The spooling directory is the given in the
sd capability. We will make three
megabytes (which is 6144 disk blocks) the amount of free disk
space that must exist on the filesystem for LPD to accept
remote jobs:
&prompt.root; echo 6144 > /var/spool/lpd/bam
boo/minfree
User restrictions
You can control which remote users can print to local
printers by specifying the rs capability in
/etc/printcap . When
rs appears in the entry for a
locally-attached printer, LPD will accept jobs from remote
hosts if the user submitting the job also
has an account of the same login name on the local host.
Otherwise, LPD refuses the job.
This capability is particularly useful in an environment
where there are (for example) different departments sharing a
network, and some users transcend departmental boundaries. By
giving them accounts on your systems, they can use your
printers from their own departmental systems. If you would
rather allow them to use only your
printers and not your compute resources, you can give them
token
accounts, with no home directory and a
useless shell like /usr/bin/false .
Accounting for Printer Usage
So, you need to charge for printouts. And why not? Paper and ink
cost money. And then there are maintenance costs—printers are
loaded with moving parts and tend to break down. You have examined
your printers, usage patterns, and maintenance fees and have come up
with a per-page (or per-foot, per-meter, or per-whatever) cost. Now,
how do you actually start accounting for printouts?
Well, the bad news is the LPD spooling system does not provide
much help in this department. Accounting is highly dependent on the
kind of printer in use, the formats being printed, and
your requirements in charging for printer
usage.
To implement accounting, you have to modify a printer's text
filter (to charge for plain text jobs) and the conversion filters (to
charge for other file formats), to count pages or query the printer
for pages printed. You cannot get away with using the simple output
filter, since it cannot do accounting. See section Filters.
Generally, there are two ways to do accounting:
Periodic accounting is the more common
way, possibly because it is easier. Whenever someone prints a
job, the filter logs the user, host, and number of pages to an
accounting file. Every month, semester, year, or whatever time
period you prefer, you collect the accounting files for the
various printers, tally up the pages printed by users, and charge
for usage. Then you truncate all the logging files, starting with
a clean slate for the next period.
Timely accounting is less common,
probably because it is more difficult. This method has the
filters charge users for printouts as soon as they use the
printers. Like disk quotas, the accounting is immediate. You can
prevent users from printing when their account goes in the red,
and might provide a way for users to check and adjust their
print quotas.
But this method requires some database
code to track users and their quotas.
The LPD spooling system supports both methods easily: since you
have to provide the filters (well, most of the time), you also have to
provide the accounting code. But there is a bright side: you have
enormous flexibility in your accounting methods. For example, you
choose whether to use periodic or timely accounting. You choose what
information to log: user names, host names, job types, pages printed,
square footage of paper used, how long the job took to print, and so
forth. And you do so by modifying the filters to save this
information.
Quick and Dirty Printer Accounting
FreeBSD comes with two programs that can get you set up with
simple periodic accounting right away. They are the text filter
lpf , described in section lpf: a Text Filter, and
&man.pac.8;, a program to gather and total
entries from printer accounting files.
As mentioned in the section on filters ( Filters), LPD starts
the text and the conversion filters with the name of the accounting
file to use on the filter command line. The filters can use this
argument to know where to write an accounting file entry. The name
of this file comes from the af capability in
/etc/printcap , and if not specified as an
absolute path, is relative to the spooling directory.
LPD starts lpf with page width and length
arguments (from the pw and pl
capabilities). lpf uses these arguments to
determine how much paper will be used. After sending the file to
the printer, it then writes an accounting entry in the accounting
file. The entries look like this:
2.00 rose:andy
3.00 rose:kelly
3.00 orchid:mary
5.00 orchid:mary
2.00 orchid:zhang
You should use a separate accounting file for each printer, as
lpf has no file locking logic built into it, and
two lpf s might corrupt each other's entries if
they were to write to the same file at the same time. A easy way to
insure a separate accounting file for each printer is to use
af=acct in /etc/printcap .
Then, each accounting file will be in the spooling directory for a
printer, in a file named acct .
When you are ready to charge users for printouts, run the
&man.pac.8; program. Just change to the spooling directory for
the printer you want to collect on and type pac .
You will get a dollar-centric summary like the following:
Login pages/feet runs price
orchid:kelly 5.00 1 $ 0.10
orchid:mary 31.00 3 $ 0.62
orchid:zhang 9.00 1 $ 0.18
rose:andy 2.00 1 $ 0.04
rose:kelly 177.00 104 $ 3.54
rose:mary 87.00 32 $ 1.74
rose:root 26.00 12 $ 0.52
total 337.00 154 $ 6.74
These are the arguments &man.pac.8; expects:
-Pprinter
Which printer to summarize.
This option works only if there is an absolute path in the
af capability in
/etc/printcap .
-c
Sort the output by cost instead of alphabetically by user
name.
-m
Ignore host name in the accounting files. With this
option, user smith on host
alpha is the same user
smith on host gamma .
Without, they are different users.
-pprice
Compute charges with price
dollars per page or per foot instead of the price from the
pc capability in
/etc/printcap , or two cents (the
default). You can specify price as
a floating point number.
-r
Reverse the sort order.
-s
Make an accounting summary file and truncate the
accounting file.
name
…
Print accounting information for the given user
names only.
In the default summary that &man.pac.8; produces, you see the
number of pages printed by each user from various hosts. If, at
your site, host does not matter (because users can use any host),
run pac -m , to produce the following
summary:
Login pages/feet runs price
andy 2.00 1 $ 0.04
kelly 182.00 105 $ 3.64
mary 118.00 35 $ 2.36
root 26.00 12 $ 0.52
zhang 9.00 1 $ 0.18
total 337.00 154 $ 6.74
To compute the dollar amount due,
&man.pac.8; uses the pc capability in the
/etc/printcap file (default of 200, or 2 cents
per page). Specify, in hundredths of cents, the price per page or
per foot you want to charge for printouts in this capability. You
can override this value when you run &man.pac.8; with the
-p option. The units for the -p
option are in dollars, though, not hundredths of cents. For
example,
&prompt.root; pac -p1.50
makes each page cost one dollar and fifty cents. You can really
rake in the profits by using this option.
Finally, running pac -s will save the summary
information in a summary accounting file, which is named the same as
the printer's accounting file, but with _sum
appended to the name. It then truncates the accounting file. When
you run &man.pac.8; again, it rereads the
summary file to get starting totals, then adds information from the
regular accounting file.
How Can You Count Pages Printed?
In order to perform even remotely accurate accounting, you need
to be able to determine how much paper a job uses. This is the
essential problem of printer accounting.
For plain text jobs, the problem is not that hard to solve: you
count how many lines are in a job and compare it to how many lines
per page your printer supports. Do not forget to take into account
backspaces in the file which overprint lines, or long logical lines
that wrap onto one or more additional physical lines.
The text filter lpf (introduced in lpf: a Text Filter) takes
into account these things when it does accounting. If you are
writing a text filter which needs to do accounting, you might want
to examine lpf 's source code.
How do you handle other file formats, though?
Well, for DVI-to-LaserJet or DVI-to-PostScript conversion, you
can have your filter parse the diagnostic output of
dvilj or dvips and look to see
how many pages were converted. You might be able to do similar
things with other file formats and conversion programs.
But these methods suffer from the fact that the printer may not
actually print all those pages. For example, it could jam, run out
of toner, or explode—and the user would still get
charged.
So, what can you do?
There is only one sure way to do
accurate accounting. Get a printer that can
tell you how much paper it uses, and attach it via a serial line or
a network connection. Nearly all PostScript printers support this
notion. Other makes and models do as well (networked Imagen laser
printers, for example). Modify the filters for these printers to
get the page usage after they print each job and have them log
accounting information based on that value
only . There is no line counting nor
error-prone file examination required.
Of course, you can always be generous and make all printouts
free.
Using Printers
This section tells you how to use printers you have setup with
FreeBSD. Here is an overview of the user-level commands:
&man.lpr.1;
Print jobs
&man.lpq.1;
Check printer queues
&man.lprm.1;
Remove jobs from a printer's queue
There is also an administrative command, &man.lpc.8;, described in
the section Administrating the LPD
Spooler, used to control printers and their queues.
All three of the commands &man.lpr.1;, &man.lprm.1;, and &man.lpq.1;
accept an option -P
printer-name to specify on which
printer/queue to operate, as listed in the
/etc/printcap file. This enables you to submit,
remove, and check on jobs for various printers. If you do not use the
-P option, then these commands use the printer
specified in the PRINTER environment variable. Finally,
if you do not have a PRINTER environment variable, these
commands default to the printer named lp .
Hereafter, the terminology default printer
means the printer named in the PRINTER environment
variable, or the printer named lp when there is no
PRINTER environment variable.
Printing Jobs
To print files, type:
&prompt.user; lpr filename ...
This prints each of the listed files to the default printer. If
you list no files, &man.lpr.1; reads data to
print from standard input. For example, this command prints some
important system files:
&prompt.user; lpr /etc/host.conf /etc/hosts.equiv
To select a specific printer, type:
&prompt.user; lpr -P printer-name filename ...
This example prints a long listing of the current directory to the
printer named rattan :
&prompt.user; ls -l | lpr -P rattan
Because no files were listed for the
&man.lpr.1; command, lpr read the data to print
from standard input, which was the output of the ls
-l command.
The &man.lpr.1; command can also accept a wide variety of options
to control formatting, apply file conversions, generate multiple
copies, and so forth. For more information, see the section Printing Options.
Checking Jobs
When you print with &man.lpr.1;, the data you wish to print is put
together in a package called a print job
, which is sent
to the LPD spooling system. Each printer has a queue of jobs, and
your job waits in that queue along with other jobs from yourself and
from other users. The printer prints those jobs in a first-come,
first-served order.
To display the queue for the default printer, type &man.lpq.1;.
For a specific printer, use the -P option. For
example, the command
&prompt.user; lpq -P bamboo
shows the queue for the printer named bamboo . Here
is an example of the output of the lpq
command:
bamboo is ready and printing
Rank Owner Job Files Total Size
active kelly 9 /etc/host.conf, /etc/hosts.equiv 88 bytes
2nd kelly 10 (standard input) 1635 bytes
3rd mary 11 ... 78519 bytes
This shows three jobs in the queue for bamboo .
The first job, submitted by user kelly, got assigned job
number
9. Every job for a printer gets a unique job number.
Most of the time you can ignore the job number, but you will need it
if you want to cancel the job; see section Removing Jobs for details.
Job number nine consists of two files; multiple files given on the
&man.lpr.1; command line are treated as part of a single job. It
is the currently active job (note the word active
under the Rank
column), which means the printer should
be currently printing that job. The second job consists of data
passed as the standard input to the &man.lpr.1; command. The third
job came from user mary ; it is a much larger
job. The pathname of the files she's trying to print is too long to
fit, so the &man.lpq.1; command just shows three dots.
The very first line of the output from &man.lpq.1; is also useful:
it tells what the printer is currently doing (or at least what LPD
thinks the printer is doing).
The &man.lpq.1; command also support a -l option
to generate a detailed long listing. Here is an example of
lpq -l :
waiting for bamboo to become ready (offline ?)
kelly: 1st [job 009rose]
/etc/host.conf 73 bytes
/etc/hosts.equiv 15 bytes
kelly: 2nd [job 010rose]
(standard input) 1635 bytes
mary: 3rd [job 011rose]
/home/orchid/mary/research/venus/alpha-regio/mapping 78519 bytes
Removing Jobs
If you change your mind about printing a job, you can remove the
job from the queue with the &man.lprm.1; command. Often, you can
even use &man.lprm.1; to remove an active job, but some or all of the
job might still get printed.
To remove a job from the default printer, first use
&man.lpq.1; to find the job number. Then type:
&prompt.user; lprm job-number
To remove the job from a specific printer, add the
-P option. The following command removes job number
10 from the queue for the printer bamboo :
&prompt.user; lprm -P bamboo 10
The &man.lprm.1; command has a few shortcuts:
lprm -
Removes all jobs (for the default printer) belonging to
you.
lprm user
Removes all jobs (for the default printer) belonging to
user . The superuser can remove other
users' jobs; you can remove only your own jobs.
lprm
With no job number, user name, or -
appearing on the command line,
&man.lprm.1; removes the currently active job on the
default printer, if it belongs to you. The superuser can remove
any active job.
Just use the -P option with the above shortcuts
to operate on a specific printer instead of the default. For example,
the following command removes all jobs for the current user in the
queue for the printer named rattan :
&prompt.user; lprm -P rattan -
If you are working in a networked environment, &man.lprm.1; will
let you remove jobs only from the
host from which the jobs were submitted, even if the same printer is
available from other hosts. The following command sequence
demonstrates this:
&prompt.user; lpr -P rattan myfile
&prompt.user; rlogin orchid
&prompt.user; lpq -P rattan
Rank Owner Job Files Total Size
active seeyan 12 ... 49123 bytes
2nd kelly 13 myfile 12 bytes
&prompt.user; lprm -P rattan 13
rose: Permission denied
&prompt.user; logout
&prompt.user; lprm -P rattan 13
dfA013rose dequeued
cfA013rose dequeued
Beyond Plain Text: Printing Options
The &man.lpr.1; command supports a number of options that control
formatting text, converting graphic and other file formats, producing
multiple copies, handling of the job, and more. This section
describes the options.
Formatting and Conversion Options
The following &man.lpr.1; options control formatting of the
files in the job. Use these options if the job does not contain
plain text or if you want plain text formatted through the
&man.pr.1; utility.
For example, the following command prints a DVI file (from the
TeX typesetting system) named fish-report.dvi
to the printer named bamboo :
&prompt.user; lpr -P bamboo -d fish-report.dvi
These options apply to every file in the job, so you cannot mix
(say) DVI and ditroff files together in a job. Instead, submit the
files as separate jobs, using a different conversion option for each
job.
All of these options except -p and
-T require conversion filters installed for the
destination printer. For example, the -d option
requires the DVI conversion filter. Section Conversion
Filters gives details.
-c
Print cifplot files.
-d
Print DVI files.
-f
Print FORTRAN text files.
-g
Print plot data.
-i number
Indent the output by number
columns; if you omit number , indent
by 8 columns. This option works only with certain conversion
filters.
Do not put any space between the -i and
the number.
-l
Print literal text data, including control
characters.
-n
Print ditroff (device independent troff) data.
-p
Format plain text with &man.pr.1; before printing. See
&man.pr.1; for more information.
-T title
Use title on the
&man.pr.1; header instead of the file name. This option has
effect only when used with the -p
option.
-t
Print troff data.
-v
Print raster data.
Here is an example: this command prints a nicely formatted
version of the &man.ls.1; manual page on the default printer:
&prompt.user; zcat /usr/share/man/man1/ls.1.gz | troff -t -man | lpr -t
The &man.zcat.1; command uncompresses the source of the
&man.ls.1; manual page and passes it to the &man.troff.1;
command, which formats that source and makes GNU troff
output and passes it to &man.lpr.1;, which submits the job
to the LPD spooler. Because we used the -t
option to &man.lpr.1;, the spooler will convert the GNU
troff output into a format the default printer can
understand when it prints the job.
Job Handling Options
The following options to &man.lpr.1; tell LPD to handle the job
specially:
-# copies
Produce a number of copies of
each file in the job instead of just one copy. An
administrator may disable this option to reduce printer
wear-and-tear and encourage photocopier usage. See section
Restricting
Multiple Copies.
This example prints three copies of
parser.c followed by three copies of
parser.h to the default printer:
&prompt.user; lpr -#3 parser.c parser.h
-m
Send mail after completing the print job. With this
option, the LPD system will send mail to your account when it
finishes handling your job. In its message, it will tell you
if the job completed successfully or if there was an error,
and (often) what the error was.
-s
Do not copy the files to the spooling directory, but make
symbolic links to them instead.
If you are printing a large job, you probably want to use
this option. It saves space in the spooling directory (your
job might overflow the free space on the filesystem where the
spooling directory resides). It saves time as well since LPD
will not have to copy each and every byte of your job to the
spooling directory.
There is a drawback, though: since LPD will refer to the
original files directly, you cannot modify or remove them
until they have been printed.
If you are printing to a remote printer, LPD will
eventually have to copy files from the local host to the
remote host, so the -s option will save
space only on the local spooling directory, not the remote.
It is still useful, though.
-r
Remove the files in the job after copying them to the
spooling directory, or after printing them with the
-s option. Be careful with this
option!
Header Page Options
These options to &man.lpr.1; adjust the text that normally
appears on a job's header page. If header pages are suppressed for
the destination printer, these options have no effect. See section
Header Pages
for information about setting up header pages.
-C text
Replace the hostname on the header page with
text . The hostname is normally the
name of the host from which the job was submitted.
-J text
Replace the job name on the header page with
text . The job name is normally the
name of the first file of the job, or
stdin if you are printing standard
input.
-h
Do not print any header page.
At some sites, this option may have no effect due to the
way header pages are generated. See Header
Pages for details.
Administrating Printers
As an administrator for your printers, you have had to install,
set up, and test them. Using the &man.lpc.8; command, you
can interact with your printers in yet more ways. With &man.lpc.8;,
you can
Start and stop the printers
Enable and disable their queues
Rearrange the order of the jobs in each queue.
First, a note about terminology: if a printer is
stopped , it will not print anything in its queue.
Users can still submit jobs, which will wait in the queue until the
printer is started or the queue is
cleared.
If a queue is disabled , no user (except root)
can submit jobs for the printer. An enabled
queue allows jobs to be submitted. A printer can be
started for a disabled queue, in which case it
will continue to print jobs in the queue until the queue is
empty.
In general, you have to have root privileges to use the
&man.lpc.8; command. Ordinary users can use the &man.lpc.8; command
to get printer status and to restart a hung printer only.
Here is a summary of the &man.lpc.8; commands. Most of the
commands takes a printer-name argument to
tell on which printer to operate. You can use all
for the printer-name to mean all printers
listed in /etc/printcap .
abort
printer-name
Cancel the current job and stop the printer. Users can
still submit jobs if the queue's enabled.
clean
printer-name
Remove old files from the printer's spooling directory.
Occasionally, the files that make up a job are not properly
removed by LPD, particularly if there have been errors during
printing or a lot of administrative activity. This command
finds files that do not belong in the spooling directory and
removes them.
disable
printer-name
Disable queuing of new jobs. If the printer's started, it
will continue to print any jobs remaining in the queue. The
superuser (root) can always submit jobs, even to a disabled
queue.
This command is useful while you are testing a new printer
or filter installation: disable the queue and submit jobs as
root. Other users will not be able to submit jobs until you
complete your testing and re-enable the queue with the
enable command.
down printer-name
message
Take a printer down. Equivalent to
disable followed by stop .
The message appears as the printer's
status whenever a user checks the printer's queue with
&man.lpq.1; or status with lpc
status .
enable
printer-name
Enable the queue for a printer. Users can submit jobs but
the printer will not print anything until it is started.
help
command-name
Print help on the command
command-name . With no
command-name , print a summary of the
commands available.
restart
printer-name
Start the printer. Ordinary users can use this command if
some extraordinary circumstance hangs LPD, but they cannot start
a printer stopped with either the stop or
down commands. The
restart command is equivalent to
abort followed by
start .
start
printer-name
Start the printer. The printer will print jobs in its
queue.
stop
printer-name
Stop the printer. The printer will finish the current job
and will not print anything else in its queue. Even though the
printer is stopped, users can still submit jobs to an enabled
queue.
topq printer-name
job-or-username
Rearrange the queue for
printer-name by placing the jobs with
the listed job numbers or the jobs
belonging to username at the top of
the queue. For this command, you cannot use
all as the
printer-name .
up
printer-name
Bring a printer up; the opposite of the
down command. Equivalent to
start followed by
enable .
&man.lpc.8; accepts the above commands on the command line. If
you do not enter any commands, &man.lpc.8; enters an interactive mode,
where you can enter commands until you type exit ,
quit , or end-of-file.
Alternatives to the Standard Spooler
If you have been reading straight through this manual, by now you
have learned just about everything there is to know about the LPD
spooling system that comes with FreeBSD. You can probably appreciate
many of its shortcomings, which naturally leads to the question:
What other spooling systems are out there (and work with
FreeBSD)?
LPRng
LPRng, which purportedly means LPR: the Next
Generation
is a complete rewrite of PLP. Patrick Powell
and Justin Mason (the principal maintainer of PLP) collaborated to
make LPRng. The main site for LPRng is http://www.astart.com/lprng/LPRng.html .
Troubleshooting
After performing the simple test with &man.lptest.1;, you might
have gotten one of the following results instead of the correct
printout:
It worked, after awhile; or, it did not eject a full
sheet.
The printer printed the above, but it sat for awhile and
did nothing. In fact, you might have needed to press a
PRINT REMAINING or FORM FEED button on the printer to get any
results to appear.
If this is the case, the printer was probably waiting to
see if there was any more data for your job before it printed
anything. To fix this problem, you can have the text filter
send a FORM FEED character (or whatever is necessary) to the
printer. This is usually sufficient to have the printer
immediately print any text remaining in its internal buffer.
It is also useful to make sure each print job ends on a full
sheet, so the next job does not start somewhere on the middle
of the last page of the previous job.
The following replacement for the shell script
/usr/local/libexec/if-simple prints a
form feed after it sends the job to the printer:
#!/bin/sh
#
# if-simple - Simple text input filter for lpd
# Installed in /usr/local/libexec/if-simple
#
# Simply copies stdin to stdout. Ignores all filter arguments.
# Writes a form feed character (\f) after printing job.
/bin/cat && printf "\f" && exit 0
exit 2
It produced the staircase effect.
You got the following on paper:
!"#$%&'()*+,-./01234
"#$%&'()*+,-./012345
#$%&'()*+,-./0123456
You have become another victim of the staircase
effect , caused by conflicting interpretations of
what characters should indicate a new line. UNIX-style
operating systems use a single character: ASCII code 10, the
line feed (LF). MS-DOS, OS/2, and others uses a pair of
characters, ASCII code 10 and ASCII code
13 (the carriage return or CR). Many printers use the MS-DOS
convention for representing new-lines.
When you print with FreeBSD, your text used just the line
feed character. The printer, upon seeing a line feed
character, advanced the paper one line, but maintained the
same horizontal position on the page for the next character
to print. That is what the carriage return is for: to move
the location of the next character to print to the left edge
of the paper.
Here is what FreeBSD wants your printer to do:
Printer received CR
Printer prints CR
Printer received LF
Printer prints CR + LF
Here are some ways to achieve this:
Use the printer's configuration switches or control
panel to alter its interpretation of these characters.
Check your printer's manual to find out how to do
this.
If you boot your system into other operating systems
besides FreeBSD, you may have to
reconfigure the printer to use a an
interpretation for CR and LF characters that those other
operating systems use. You might prefer one of the other
solutions, below.
Have FreeBSD's serial line driver automatically
convert LF to CR+LF. Of course, this works with printers
on serial ports only . To enable this
feature, set the CRMOD bit in fs
capability in the /etc/printcap file
for the printer.
Send an escape code to the
printer to have it temporarily treat LF characters
differently. Consult your printer's manual for escape
codes that your printer might support. When you find the
proper escape code, modify the text filter to send the
code first, then send the print job.
Here is an example text filter for printers that
understand the Hewlett-Packard PCL escape codes. This
filter makes the printer treat LF characters as a LF and
CR; then it sends the job; then it sends a form feed to
eject the last page of the job. It should work with
nearly all Hewlett Packard printers.
#!/bin/sh
#
# hpif - Simple text input filter for lpd for HP-PCL based printers
# Installed in /usr/local/libexec/hpif
#
# Simply copies stdin to stdout. Ignores all filter arguments.
# Tells printer to treat LF as CR+LF. Ejects the page when done.
printf "\033&k2G" && cat && printf "\033&l0H" && exit 0
exit 2
Here is an example /etc/printcap
from a host called orchid. It has a single printer
attached to its first parallel port, a Hewlett Packard
LaserJet 3Si named teak . It is using the
above script as its text filter:
#
# /etc/printcap for host orchid
#
teak|hp|laserjet|Hewlett Packard LaserJet 3Si:\
:lp=/dev/lpt0:sh:sd=/var/spool/lpd/teak:mx#0:\
:if=/usr/local/libexec/hpif:
It overprinted each line.
The printer never advanced a line. All of the lines of
text were printed on top of each other on one line.
This problem is the opposite
of the
staircase effect, described above, and is much rarer.
Somewhere, the LF characters that FreeBSD uses to end a line
are being treated as CR characters to return the print
location to the left edge of the paper, but not also down a
line.
Use the printer's configuration switches or control panel
to enforce the following interpretation of LF and CR
characters:
Printer receives
Printer prints
CR
CR
LF
CR + LF
The printer lost characters.
While printing, the printer did not print a few characters
in each line. The problem might have gotten worse as the
printer ran, losing more and more characters.
The problem is that the printer cannot keep up with the
speed at which the computer sends data over a serial line
(this problem should not occur with printers on parallel
ports). There are two ways to overcome the problem:
If the printer supports XON/XOFF flow control, have
FreeBSD use it by specifying the TANDEM bit in the
fs capability.
If the printer supports carrier flow control, specify
the MDMBUF bit in the fs capability.
Make sure the cable connecting the printer to the computer
is correctly wired for carrier flow control.
If the printer does not support any flow control, use
some combination of the NLDELAY, TBDELAY, CRDELAY, VTDELAY,
and BSDELAY bits in the fs capability
to add appropriate delays to the stream of data sent to
the printer.
It printed garbage.
The printer printed what appeared to be random garbage,
but not the desired text.
This is usually another symptom of incorrect
communications parameters with a serial printer. Double-check
the bps rate in the br capability, and the
parity bits in the fs and
fc capabilities; make sure the printer is
using the same settings as specified in the
/etc/printcap file.
Nothing happened.
If nothing happened, the problem is probably within
FreeBSD and not the hardware. Add the log file
(lf ) capability to the entry for the
printer you are debugging in the
/etc/printcap file. For example, here is
the entry for rattan , with the
lf capability:
rattan|line|diablo|lp|Diablo 630 Line Printer:\
:sh:sd=/var/spool/lpd/rattan:\
:lp=/dev/lpt0:\
:if=/usr/local/libexec/if-simple:\
:lf=/var/log/rattan.log
Then, try printing again. Check the log file (in our
example, /var/log/rattan.log ) to see any
error messages that might appear. Based on the messages you
see, try to correct the problem.
If you do not specify a lf capability,
LPD uses /dev/console as a
default.
diff --git a/en_US.ISO8859-1/books/handbook/security/chapter.sgml b/en_US.ISO8859-1/books/handbook/security/chapter.sgml
index af2d3d1704..7269fc48af 100644
--- a/en_US.ISO8859-1/books/handbook/security/chapter.sgml
+++ b/en_US.ISO8859-1/books/handbook/security/chapter.sgml
@@ -1,2921 +1,2921 @@
Security
Much of this chapter has been taken from the
&man.security.7; man page, originally written by
&a.dillon;.
Synopsis
The following chapter will provide a basic introduction to
system security concepts, some general good rules of thumb, and some
advanced topics such as S/Key, OpenSSL, Kerberos, and others.
Introduction
Security is a function that begins and ends with the system
administrator. While all BSD UNIX multi-user systems have some
inherent security, the job of building and maintaining additional
security mechanisms to keep those users honest
is
probably one of the single largest undertakings of the sysadmin.
Machines are only as secure as you make them, and security concerns
are ever competing with the human necessity for convenience. UNIX
systems, in general, are capable of running a huge number of
simultaneous processes and many of these processes operate as
servers – meaning that external entities can connect and talk
to them. As yesterday's mini-computers and mainframes become
today's desktops, and as computers become networked and
internetworked, security becomes an ever bigger issue.
Security is best implemented through a layered
onion
approach. In a nutshell, what you want to do is
to create as many layers of security as are convenient and then
carefully monitor the system for intrusions. You do not want to
overbuild your security or you will interfere with the detection
side, and detection is one of the single most important aspects of
any security mechanism. For example, it makes little sense to set
the schg flags (see &man.chflags.1;) on every system binary because
while this may temporarily protect the binaries, it prevents an
attacker who has broken in from making an easily detectable change
that may result in your security mechanisms not detecting the attacker
at all.
System security also pertains to dealing with various forms of
attack, including attacks that attempt to crash or otherwise make a
system unusable but do not attempt to break root. Security concerns
can be split up into several categories:
Denial of service attacks.
User account compromises.
Root compromise through accessible servers.
Root compromise via user accounts.
Backdoor creation.
A denial of service attack is an action that deprives the
machine of needed resources. Typically, D.O.S. attacks are
brute-force mechanisms that attempt to crash or otherwise make a
machine unusable by overwhelming its servers or network stack. Some
D.O.S. attacks try to take advantages of bugs in the networking
stack to crash a machine with a single packet. The latter can only
be fixed by applying a bug fix to the kernel. Attacks on servers
can often be fixed by properly specifying options to limit the load
the servers incur on the system under adverse conditions.
Brute-force network attacks are harder to deal with. A
spoofed-packet attack, for example, is nearly impossible to stop
short of cutting your system off from the Internet. It may not be
able to take your machine down, but it can saturate your
Internet connection.
A user account compromise is even more common then a D.O.S.
attack. Many sysadmins still run standard telnetd, rlogind, rshd,
and ftpd servers on their machines. These servers, by default, do
not operate over encrypted connections. The result is that if you
have any moderate-sized user base, one or more of your users logging
into your system from a remote location (which is the most common
and convenient way to login to a system) will have his or her
password sniffed. The attentive system admin will analyze his
remote access logs looking for suspicious source addresses even for
successful logins.
One must always assume that once an attacker has access to a
user account, the attacker can break root. However, the reality is
that in a well secured and maintained system, access to a user
account does not necessarily give the attacker access to root. The
distinction is important because without access to root the attacker
cannot generally hide his tracks and may, at best, be able to do
nothing more then mess with the user's files or crash the machine.
User account compromises are very common because users tend not to
take the precautions that sysadmins take.
System administrators must keep in mind that there are
potentially many ways to break root on a machine. The attacker
may know the root password, the attacker may find a bug in a
root-run server and be able to break root over a network
connection to that server, or the attacker may know of a bug in
an suid-root program that allows the attacker to break root once
he has broken into a user's account. If an attacker has found a
a way to break root on a machine, the attacker may not have a need
to install a backdoor. Many of the root holes
found and closed to date involve a considerable amount of work
by the attacker to cleanup after himself, so most attackers install
backdoors. Backdoors provide the attacker with a way to easily
regain root access to the system, but it also gives the smart
system administrator a convenient way to detect the intrusion.
Making it impossible for an attacker to install a backdoor may
actually be detrimental to your security because it will not
close off the hole the attacker found to break in the first
place.
Security remedies should always be implemented with a
multi-layered onion peel
approach and can be
categorized as follows:
Securing root and staff accounts.
Securing root – root-run servers and suid/sgid
binaries.
Securing user accounts.
Securing the password file.
Securing the kernel core, raw devices, and
filesystems.
Quick detection of inappropriate changes made to the
system.
Paranoia.
The next section of this chapter will cover the above bullet
items in greater depth.
Securing FreeBSD
The sections that follow will cover the methods of securing your
FreeBSD system that were mentioned in the last section of this chapter.
Securing the root account and staff accounts
First off, do not bother securing staff accounts if you have
not secured the root account. Most systems have a password
assigned to the root account. The first thing you do is assume
that the password is always compromised.
This does not mean that you should remove the password. The
password is almost always necessary for console access to the
machine. What it does mean is that you should not make it
possible to use the password outside of the console or possibly
even with the &man.su.1; command. For example, make sure that
your pty's are specified as being unsecure in the
/etc/ttys file so that direct root logins
via telnet or rlogin are
disallowed. If using other login services such as
sshd , make sure that direct root logins
are disabled there as well. Consider every access method –
services such as FTP often fall through the cracks. Direct root
logins should only be allowed via the system console.
Of course, as a sysadmin you have to be able to get to root,
so we open up a few holes. But we make sure these holes require
additional password verification to operate. One way to make root
accessible is to add appropriate staff accounts to the
wheel group (in
/etc/group ). The staff members placed in the
wheel group are allowed to
su to root. You should never give staff
members native wheel access by putting them in the
wheel group in their password entry. Staff
accounts should be placed in a staff group, and
then added to the wheel group via the
/etc/group file. Only those staff members
who actually need to have root access should be placed in the
wheel group. It is also possible, when using
an authentication method such as kerberos, to use kerberos'
.k5login file in the root account to allow a
&man.ksu.1; to root without having to place anyone at all in the
wheel group. This may be the better solution
since the wheel mechanism still allows an
intruder to break root if the intruder has gotten hold of your
password file and can break into a staff account. While having
the wheel mechanism is better then having
nothing at all, it is not necessarily the safest option.
An indirect way to secure staff accounts, and ultimately
root access is to use an alternative login access method and
do what is known as * 'ing out the crypted
password for the staff accounts. Using the &man.vipw.8;
command, one can replace each instance of a crypted password
with a single * character. This command
will update the /etc/master.passwd file
and user/password database to disable password-authenticated
logins.
A staff account entry such as:
foobar:R9DT/Fa1/LV9U:1000:1000::0:0:Foo Bar:/home/foobar:/usr/local/bin/tcsh
Should be changed to this :
foobar:*:1000:1000::0:0:Foo Bar:/home/foobar:/usr/local/bin/tcsh
This change will prevent normal logins from occurring,
since the encrypted password will never match
* . With this done, staff members must use
another mechanism to authenticate themselves such as
&man.kerberos.1; or &man.ssh.1; using a public/private key
pair. When using something like kerberos, one generally must
secure the machines which run the kerberos servers and your
desktop workstation. When using a public/private key pair
with ssh , one must generally secure
the machine used to login from (typically
one's workstation). An additional layer of protection can be
added to the key pair by password protecting the key pair when
creating it with &man.ssh-keygen.1;. Being able to
* out the passwords for staff accounts also
guarantees that staff members can only login through secure
access methods that you have setup. This forces all staff
members to use secure, encrypted connections for all of their
sessions which closes an important hole used by many
intruders: That of sniffing the network from an unrelated,
less secure machine.
The more indirect security mechanisms also assume that you are
logging in from a more restrictive server to a less restrictive
server. For example, if your main box is running all sorts of
servers, your workstation should not be running any. In order for
your workstation to be reasonably secure you should run as few
servers as possible, up to and including no servers at all, and
you should run a password-protected screen blanker. Of course,
given physical access to a workstation an attacker can break any
sort of security you put on it. This is definitely a problem that
you should consider but you should also consider the fact that the
vast majority of break-ins occur remotely, over a network, from
people who do not have physical access to your workstation or
servers.
Using something like kerberos also gives you the ability to
disable or change the password for a staff account in one place
and have it immediately effect all the machine the staff member
may have an account on. If a staff member's account gets
compromised, the ability to instantly change his password on all
machines should not be underrated. With discrete passwords,
changing a password on N machines can be a mess. You can also
impose re-passwording restrictions with kerberos: not only can a
kerberos ticket be made to timeout after a while, but the kerberos
system can require that the user choose a new password after a
certain period of time (say, once a month).
Securing Root-run Servers and SUID/SGID Binaries
The prudent sysadmin only runs the servers he needs to, no
more, no less. Be aware that third party servers are often the
most bug-prone. For example, running an old version of imapd or
popper is like giving a universal root ticket out to the entire
world. Never run a server that you have not checked out
carefully. Many servers do not need to be run as root. For
example, the ntalk ,
comsat , and
finger daemons can be run in special
user sandboxes . A sandbox isn't perfect unless
you go to a large amount of trouble, but the onion approach to
security still stands: If someone is able to break in through
a server running in a sandbox, they still have to break out of the
sandbox. The more layers the attacker must break through, the
lower the likelihood of his success. Root holes have historically
been found in virtually every server ever run as root, including
basic system servers. If you are running a machine through which
people only login via sshd and never
login via telnetd or
rshd or
rlogind , then turn off those
services!
FreeBSD now defaults to running
ntalkd ,
comsat , and
finger in a sandbox. Another program
which may be a candidate for running in a sandbox is &man.named.8;.
/etc/defaults/rc.conf includes the arguments
necessary to run named in a sandbox in a
commented-out form. Depending on whether you are installing a new
system or upgrading an existing system, the special user accounts
used by these sandboxes may not be installed. The prudent
sysadmin would research and implement sandboxes for servers
whenever possible.
There are a number of other servers that typically do not run
in sandboxes: sendmail ,
popper ,
imapd , ftpd ,
and others. There are alternatives to some of these, but
installing them may require more work then you are willing to
perform (the convenience factor strikes again). You may have to
run these servers as root and rely on other mechanisms to detect
break-ins that might occur through them.
The other big potential root hole in a system are the
suid-root and sgid binaries installed on the system. Most of
these binaries, such as rlogin , reside
in /bin , /sbin ,
/usr/bin , or /usr/sbin .
While nothing is 100% safe, the system-default suid and sgid
binaries can be considered reasonably safe. Still, root holes are
occasionally found in these binaries. A root hole was found in
Xlib in 1998 that made
xterm (which is typically suid)
vulnerable. It is better to be safe then sorry and the prudent
sysadmin will restrict suid binaries that only staff should run to
a special group that only staff can access, and get rid of
(chmod 000 ) any suid binaries that nobody uses.
A server with no display generally does not need an
xterm binary. Sgid binaries can be
almost as dangerous. If an intruder can break an sgid-kmem binary
the intruder might be able to read /dev/kmem
and thus read the crypted password file, potentially compromising
any passworded account. Alternatively an intruder who breaks
group kmem can monitor keystrokes sent through
pty's, including pty's used by users who login through secure
methods. An intruder that breaks the tty group can write to
almost any user's tty. If a user is running a terminal program or
emulator with a keyboard-simulation feature, the intruder can
potentially generate a data stream that causes the user's terminal
to echo a command, which is then run as that user.
Securing User Accounts
User accounts are usually the most difficult to secure. While
you can impose Draconian access restrictions on your staff and
* out their passwords, you may not be able to
do so with any general user accounts you might have. If you do
have sufficient control then you may win out and be able to secure
the user accounts properly. If not, you simply have to be more
vigilant in your monitoring of those accounts. Use of
ssh and kerberos for user accounts is
more problematic due to the extra administration and technical
support required, but still a very good solution compared to a
crypted password file.
Securing the Password File
The only sure fire way is to * out as many
passwords as you can and use ssh or
kerberos for access to those accounts. Even though the crypted
password file (/etc/spwd.db ) can only be read
by root, it may be possible for an intruder to obtain read access
to that file even if the attacker cannot obtain root-write
access.
Your security scripts should always check for and report
changes to the password file (see Checking file integrity
below).
Securing the Kernel Core, Raw Devices, and
Filesystems
If an attacker breaks root he can do just about anything, but
there are certain conveniences. For example, most modern kernels
have a packet sniffing device driver built in. Under FreeBSD it
is called the bpf device. An intruder
will commonly attempt to run a packet sniffer on a compromised
machine. You do not need to give the intruder the capability and
most systems should not have the bpf device compiled in.
But even if you turn off the bpf device, you still have
/dev/mem and /dev/kmem
to worry about. For that matter, the intruder can still write to
raw disk devices. Also, there is another kernel feature called
the module loader, &man.kldload.8;. An enterprising intruder can
use a KLD module to install his own bpf device or other sniffing
device on a running kernel. To avoid these problems you have to
run the kernel at a higher secure level, at least securelevel 1.
The securelevel can be set with a sysctl on
the kern.securelevel variable. Once you have
set the securelevel to 1, write access to raw devices will be
denied and special chflags flags, such as schg ,
will be enforced. You must also ensure that the
schg flag is set on critical startup binaries,
directories, and script files – everything that gets run up
to the point where the securelevel is set. This might be overdoing
it, and upgrading the system is much more difficult when you
operate at a higher secure level. You may compromise and run the
system at a higher secure level but not set the
schg flag for every system file and directory
under the sun. Another possibility is to simply mount
/ and /usr read-only.
It should be noted that being too draconian in what you attempt to
protect may prevent the all-important detection of an
intrusion.
Checking File Integrity: Binaries, Configuration Files,
Etc.
When it comes right down to it, you can only protect your core
system configuration and control files so much before the
convenience factor rears its ugly head. For example, using
chflags to set the schg bit
on most of the files in / and
/usr is probably counterproductive because
while it may protect the files, it also closes a detection window.
The last layer of your security onion is perhaps the most
important – detection. The rest of your security is pretty
much useless (or, worse, presents you with a false sense of
safety) if you cannot detect potential incursions. Half the job
of the onion is to slow down the attacker rather then stop him in
order to give the detection side of the equation a chance to catch
him in the act.
The best way to detect an incursion is to look for modified,
missing, or unexpected files. The best way to look for modified
files is from another (often centralized) limited-access system.
Writing your security scripts on the extra-secure limited-access
system makes them mostly invisible to potential attackers, and this
is important. In order to take maximum advantage you generally
have to give the limited-access box significant access to the
other machines in the business, usually either by doing a
read-only NFS export of the other machines to the limited-access
box, or by setting up ssh key-pairs to
allow the limit-access box to ssh to
the other machines. Except for its network traffic, NFS is the
least visible method – allowing you to monitor the
filesystems on each client box virtually undetected. If your
limited-access server is connected to the client boxes through a
switch, the NFS method is often the better choice. If your
limited-access server is connected to the client boxes through a
hub or through several layers of routing, the NFS method may be
too insecure (network-wise) and using
ssh may be the better choice even with
the audit-trail tracks that ssh
lays.
Once you give a limit-access box at least read access to the
client systems it is supposed to monitor, you must write scripts
to do the actual monitoring. Given an NFS mount, you can write
scripts out of simple system utilities such as &man.find.1; and
&man.md5.1;. It is best to physically md5 the client-box files
boxes at least once a day, and to test control files such as those
found in /etc and
/usr/local/etc even more often. When
mismatches are found relative to the base md5 information the
limited-access machine knows is valid, it should scream at a
sysadmin to go check it out. A good security script will also
check for inappropriate suid binaries and for new or deleted files
on system partitions such as / and
/usr .
When using ssh rather then NFS,
writing the security script is much more difficult. You
essentially have to scp the scripts to the client box in order to
run them, making them visible, and for safety you also need to
scp the binaries (such as find) that those
scripts use. The ssh daemon on the
client box may already be compromised. All in all, using
ssh may be necessary when running over
unsecure links, but it's also a lot harder to deal with.
A good security script will also check for changes to user and
staff members access configuration files:
.rhosts , .shosts ,
.ssh/authorized_keys and so forth…
files that might fall outside the purview of the
MD5 check.
If you have a huge amount of user disk space it may take too
long to run through every file on those partitions. In this case,
setting mount flags to disallow suid binaries and devices on those
partitions is a good idea. The nodev and
nosuid options (see &man.mount.8;) are what you
want to look into. You should probably scan them anyway at least
once a week, since the object of this layer is to detect a break-in
whether or not the break-in is effective.
Process accounting (see &man.accton.8;) is a relatively
low-overhead feature of the operating system which might help
as a post-break-in evaluation mechanism. It is especially
useful in tracking down how an intruder has actually broken into
a system, assuming the file is still intact after the break-in
occurs.
Finally, security scripts should process the log files and the
logs themselves should be generated in as secure a manner as
possible – remote syslog can be very useful. An intruder
tries to cover his tracks, and log files are critical to the
sysadmin trying to track down the time and method of the initial
break-in. One way to keep a permanent record of the log files is
to run the system console to a serial port and collect the
information on a continuing basis through a secure machine
monitoring the consoles.
Paranoia
A little paranoia never hurts. As a rule, a sysadmin can add
any number of security features as long as they do not effect
convenience, and can add security features that do effect
convenience with some added thought. Even more importantly, a
security administrator should mix it up a bit – if you use
recommendations such as those given by this document verbatim, you
give away your methodologies to the prospective attacker who also
has access to this document.
Denial of Service Attacks
This section covers Denial of Service attacks. A DOS attack
is typically a packet attack. While there is not much you can do
about modern spoofed packet attacks that saturate your network,
you can generally limit the damage by ensuring that the attacks
cannot take down your servers.
Limiting server forks.
Limiting springboard attacks (ICMP response attacks, ping
broadcast, etc.).
Kernel Route Cache.
A common DOS attack is against a forking server that attempts
to cause the server to eat processes, file descriptors, and memory
until the machine dies. Inetd (see &man.inetd.8;) has several
options to limit this sort of attack. It should be noted that
while it is possible to prevent a machine from going down it is
not generally possible to prevent a service from being disrupted
by the attack. Read the inetd manual page carefully and pay
specific attention to the -c , -C ,
and -R options. Note that spoofed-IP attacks
will circumvent the -C option to inetd, so
typically a combination of options must be used. Some standalone
servers have self-fork-limitation parameters.
Sendmail has its
-OMaxDaemonChildren option which tends to work
much better than trying to use sendmail's load limiting options
due to the load lag. You should specify a
MaxDaemonChildren parameter when you start
sendmail high enough to handle your
expected load but no so high that the computer cannot handle that
number of sendmails without falling on
its face. It is also prudent to run sendmail in queued mode
(-ODeliveryMode=queued ) and to run the daemon
(sendmail -bd ) separate from the queue-runs
(sendmail -q15m ). If you still want real-time
delivery you can run the queue at a much lower interval, such as
-q1m , but be sure to specify a reasonable
MaxDaemonChildren option for that sendmail to
prevent cascade failures.
Syslogd can be attacked directly
and it is strongly recommended that you use the -s
option whenever possible, and the -a option
otherwise.
You should also be fairly careful with connect-back services
such as tcpwrapper 's reverse-identd,
which can be attacked directly. You generally do not want to use
the reverse-ident feature of
tcpwrappers for this reason.
It is a very good idea to protect internal services from
external access by firewalling them off at your border routers.
The idea here is to prevent saturation attacks from outside your
LAN, not so much to protect internal services from network-based
root compromise. Always configure an exclusive firewall, i.e.,
firewall everything except ports A, B,
C, D, and M-Z
. This way you can firewall off all of your
low ports except for certain specific services such as
named (if you are primary for a zone),
ntalkd ,
sendmail , and other Internet-accessible
services. If you try to configure the firewall the other way
– as an inclusive or permissive firewall, there is a good
chance that you will forget to close
a couple of
services or that you will add a new internal service and forget
to update the firewall. You can still open up the high-numbered
port range on the firewall to allow permissive-like operation
without compromising your low ports. Also take note that FreeBSD
allows you to control the range of port numbers used for dynamic
binding via the various net.inet.ip.portrange
sysctl 's (sysctl -a | fgrep
portrange ), which can also ease the complexity of your
firewall's configuration. For example, you might use a normal
first/last range of 4000 to 5000, and a hiport range of 49152 to
65535, then block everything under 4000 off in your firewall
(except for certain specific Internet-accessible ports, of
course).
Another common DOS attack is called a springboard attack
– to attack a server in a manner that causes the server to
generate responses which then overload the server, the local
network, or some other machine. The most common attack of this
nature is the ICMP ping broadcast attack .
The attacker spoofs ping packets sent to your LAN's broadcast
address with the source IP address set to the actual machine they
wish to attack. If your border routers are not configured to
stomp on ping's to broadcast addresses, your LAN winds up
generating sufficient responses to the spoofed source address to
saturate the victim, especially when the attacker uses the same
trick on several dozen broadcast addresses over several dozen
different networks at once. Broadcast attacks of over a hundred
and twenty megabits have been measured. A second common
springboard attack is against the ICMP error reporting system.
By constructing packets that generate ICMP error responses, an
attacker can saturate a server's incoming network and cause the
server to saturate its outgoing network with ICMP responses. This
type of attack can also crash the server by running it out of
mbuf's, especially if the server cannot drain the ICMP responses
it generates fast enough. The FreeBSD kernel has a new kernel
compile option called ICMP_BANDLIM which limits the effectiveness
of these sorts of attacks. The last major class of springboard
attacks is related to certain internal inetd services such as the
udp echo service. An attacker simply spoofs a UDP packet with the
source address being server A's echo port, and the destination
address being server B's echo port, where server A and B are both
on your LAN. The two servers then bounce this one packet back and
forth between each other. The attacker can overload both servers
and their LANs simply by injecting a few packets in this manner.
Similar problems exist with the internal chargen port. A
competent sysadmin will turn off all of these inetd-internal test
services.
Spoofed packet attacks may also be used to overload the kernel
route cache. Refer to the net.inet.ip.rtexpire ,
rtminexpire , and rtmaxcache
sysctl parameters. A spoofed packet attack
that uses a random source IP will cause the kernel to generate a
temporary cached route in the route table, viewable with
netstat -rna | fgrep W3 . These routes
typically timeout in 1600 seconds or so. If the kernel detects
that the cached route table has gotten too big it will dynamically
reduce the rtexpire but will never decrease it to less then
rtminexpire. There are two problems:
The kernel does not react quickly enough when a lightly
loaded server is suddenly attacked.
The rtminexpire is not low enough for
the kernel to survive a sustained attack.
If your servers are connected to the Internet via a T3 or
better it may be prudent to manually override both
rtexpire and rtminexpire
via &man.sysctl.8;. Never set either parameter to zero (unless
you want to crash the machine :-). Setting both
parameters to 2 seconds should be sufficient to protect the route
table from attack.
Access Issues with Kerberos and SSH
There are a few issues with both kerberos and
ssh that need to be addressed if
you intend to use them. Kerberos V is an excellent
authentication protocol but there are bugs in the kerberized
telnet and
rlogin applications that make them
unsuitable for dealing with binary streams. Also, by default
kerberos does not encrypt a session unless you use the
-x option. ssh
encrypts everything by default.
ssh works quite well in every
respect except that it forwards encryption keys by default. What
this means is that if you have a secure workstation holding keys
that give you access to the rest of the system, and you
ssh to an unsecure machine, your keys
becomes exposed. The actual keys themselves are not exposed, but
ssh installs a forwarding port for the
duration of your login and if a attacker has broken root on the
unsecure machine he can utilize that port to use your keys to gain
access to any other machine that your keys unlock.
We recommend that you use ssh in
combination with kerberos whenever possible for staff logins.
ssh can be compiled with kerberos
support. This reduces your reliance on potentially exposable
ssh keys while at the same time
protecting passwords via kerberos. ssh
keys should only be used for automated tasks from secure machines
(something that kerberos is unsuited to). We also recommend that
you either turn off key-forwarding in the
ssh configuration, or that you make use
of the from=IP/DOMAIN option that
ssh allows in its
authorized_keys file to make the key only
usable to entities logging in from specific machines.
DES, MD5, and Crypt
Parts rewritten and updated by &a.unfurl;, 21 March
2000.
Every user on a UNIX system has a password associated with
their account. It seems obvious that these passwords need to be
known only to the user and the actual operating system. In
order to keep these passwords secret, they are encrypted with
what is known as a one-way hash
, that is, they can
only be easily encrypted but not decrypted. In other words, what
we told you a moment ago was obvious is not even true: the
operating system itself does not really know
the password. It only knows the encrypted
form of the password. The only way to get the
plain-text
password is by a brute force search of the
space of possible passwords.
Unfortunately the only secure way to encrypt passwords when
UNIX came into being was based on DES, the Data Encryption
Standard. This is not such a problem for users that live in
the US, but since the source code for DES could not be exported
outside the US, FreeBSD had to find a way to both comply with
US law and retain compatibility with all the other UNIX
variants that still use DES.
The solution was to divide up the encryption libraries
so that US users could install the DES libraries and use
DES but international users still had an encryption method
that could be exported abroad. This is how FreeBSD came to
use MD5 as its default encryption method. MD5 is believed to
be more secure than DES, so installing DES is offered primarily
for compatibility reasons.
Recognizing your crypt mechanism
It is pretty easy to identify which encryption method
FreeBSD is set up to use. Examining the encrypted passwords in
the /etc/master.passwd file is one way.
Passwords encrypted with the MD5 hash are longer than those with
encrypted with the DES hash and also begin with the characters
$1$ . DES password strings do not
have any particular identifying characteristics, but they are
shorter than MD5 passwords, and are coded in a 64-character
alphabet which does not include the $
character, so a relatively short string which does not begin with
a dollar sign is very likely a DES password.
The libraries can identify the passwords this way as well.
As a result, the DES libraries are able to identify MD5
passwords, and use MD5 to check passwords that were encrypted
that way, and DES for the rest. They are able to do this
because the DES libraries also contain MD5. Unfortunately, the
reverse is not true, so the MD5 libraries cannot authenticate
passwords that were encrypted with DES.
Identifying which library is being used by the programs on
your system is easy as well. Any program that uses crypt is linked
against libcrypt which for each type of library is a symbolic link
to the appropriate implementation. For example, on a system using
the DES versions:
&prompt.user; ls -l /usr/lib/libcrypt*
lrwxr-xr-x 1 root wheel 13 Mar 19 06:56 libcrypt.a -> libdescrypt.a
lrwxr-xr-x 1 root wheel 18 Mar 19 06:56 libcrypt.so.2.0 -> libdescrypt.so.2.0
lrwxr-xr-x 1 root wheel 15 Mar 19 06:56 libcrypt_p.a -> libdescrypt_p.a
On a system using the MD5-based libraries, the same links will
be present, but the target will be libscrypt
rather than libdescrypt .
If you have installed the DES-capable crypt library
libdescrypt (e.g. by installing the
"crypto" distribution), then which password format will be used
for new passwords is controlled by the
passwd_format
login capability in
/etc/login.conf , which takes values of
either des
or md5
. See the
&man.login.conf.5; manpage for more information about login
capabilities.
S/Key
S/Key is a one-time password scheme based on a one-way hash
function. FreeBSD uses the MD4 hash for compatibility but other
systems have used MD5 and DES-MAC. S/Key has been part of the
FreeBSD base system since version 1.1.5 and is also used on a
growing number of other operating systems. S/Key is a registered
trademark of Bell Communications Research, Inc.
There are three different sorts of passwords which we will talk
about in the discussion below. The first is your usual UNIX-style or
Kerberos password; we will call this a UNIX password
.
The second sort is the one-time password which is generated by the
S/Key key program and accepted by the
keyinit program and the login prompt; we will
call this a one-time password
. The final sort of
password is the secret password which you give to the
key program (and sometimes the
keyinit program) which it uses to generate
one-time passwords; we will call it a secret password
or just unqualified password
.
The secret password does not have anything to do with your UNIX
password; they can be the same but this is not recommended. S/Key
secret passwords are not limited to 8 characters like UNIX passwords,
they can be as long as you like. Passwords of six or seven word
long phrases are fairly common. For the most part, the S/Key system
operates completely independently of the UNIX password
system.
Besides the password, there are two other pieces of data that
are important to S/Key. One is what is known as the
seed
or key
and consists of two letters
and five digits. The other is what is called the iteration
count
and is a number between 1 and 100. S/Key creates the
one-time password by concatenating the seed and the secret password,
then applying the MD4 hash as many times as specified by the
iteration count and turning the result into six short English words.
These six English words are your one-time password. The
login and su programs keep
track of the last one-time password used, and the user is
authenticated if the hash of the user-provided password is equal to
the previous password. Because a one-way hash is used it is
impossible to generate future one-time passwords if a successfully
used password is captured; the iteration count is decremented after
each successful login to keep the user and the login program in
sync. When the iteration count gets down to 1 S/Key must be
reinitialized.
There are four programs involved in the S/Key system which we
will discuss below. The key program accepts an
iteration count, a seed, and a secret password, and generates a
one-time password. The keyinit program is used
to initialized S/Key, and to change passwords, iteration counts, or
seeds; it takes either a secret password, or an iteration count,
seed, and one-time password. The keyinfo program
examines the /etc/skeykeys file and prints out
the invoking user's current iteration count and seed. Finally, the
login and su programs contain
the necessary logic to accept S/Key one-time passwords for
authentication. The login program is also
capable of disallowing the use of UNIX passwords on connections
coming from specified addresses.
There are four different sorts of operations we will cover. The
first is using the keyinit program over a secure
connection to set up S/Key for the first time, or to change your
password or seed. The second operation is using the
keyinit program over an insecure connection, in
conjunction with the key program over a secure
connection, to do the same. The third is using the
key program to log in over an insecure
connection. The fourth is using the key program
to generate a number of keys which can be written down or printed
out to carry with you when going to some location without secure
connections to anywhere.
Secure connection initialization
To initialize S/Key for the first time, change your password,
or change your seed while logged in over a secure connection
(e.g., on the console of a machine or via ssh), use the
keyinit command without any parameters while
logged in as yourself:
&prompt.user; keyinit
Adding unfurl:
Reminder - Only use this method if you are directly connected.
If you are using telnet or rlogin exit with no password and use keyinit -s.
Enter secret password:
Again secret password:
ID unfurl s/key is 99 to17757
DEFY CLUB PRO NASH LACE SOFT
At the Enter secret password: prompt you
should enter a password or phrase. Remember, this is not the
password that you will use to login with, this is used to generate
your one-time login keys. The ID
line gives the
parameters of your particular S/Key instance; your login name, the
iteration count, and seed. When logging in with S/Key, the system
will remember these parameters and present them back to you so you
do not have to remember them. The last line gives the particular
one-time password which corresponds to those parameters and your
secret password; if you were to re-login immediately, this
one-time password is the one you would use.
Insecure connection initialization
To initialize S/Key or change your secret password over an
insecure connection, you will need to already have a secure
connection to some place where you can run the
key program; this might be in the form of a
desk accessory on a Macintosh, or a shell prompt on a machine you
trust. You will also need to make up an iteration count (100 is
probably a good value), and you may make up your own seed or use a
randomly-generated one. Over on the insecure connection (to the
machine you are initializing), use the keyinit
-s command:
&prompt.user; keyinit -s
Updating unfurl:
Old key: to17758
Reminder you need the 6 English words from the key command.
Enter sequence count from 1 to 9999: 100
Enter new key [default to17759]:
s/key 100 to 17759
s/key access password:
To accept the default seed (which the
keyinit program confusingly calls a
key ), press return. Then before entering an
access password, move over to your secure connection or S/Key desk
accessory, and give it the same parameters:
&prompt.user; key 100 to17759
Reminder - Do not use this program while logged in via telnet or rlogin.
Enter secret password: <secret password>
CURE MIKE BANE HIM RACY GORE
Now switch back over to the insecure connection, and copy the
one-time password generated by key over to the
keyinit program:
s/key access password:CURE MIKE BANE HIM RACY GORE
ID unfurl s/key is 100 to17759
CURE MIKE BANE HIM RACY GORE
The rest of the description from the previous section applies
here as well.
Generating a single one-time password
Once you've initialized S/Key, when you login you will be
presented with a prompt like this:
&prompt.user; telnet example.com
Trying 10.0.0.1...
Connected to example.com
Escape character is '^]'.
FreeBSD/i386 (example.com) (ttypa)
login: <username>
s/key 97 fw13894
Password:
As a side note, the S/Key prompt has a useful feature
(not shown here): if you press return at the password prompt, the
login program will turn echo on, so you can see what you are
typing. This can be extremely useful if you are attempting to
type in an S/Key by hand, such as from a printout. Also, if this
machine were configured to disallow UNIX passwords over a
connection from the source machine, the prompt would have also included
the annotation (s/key required) , indicating
that only S/Key one-time passwords will be accepted.
At this point you need to generate your one-time password to
answer this login prompt. This must be done on a trusted system
that you can run the key command on. (There
are versions of the key program from DOS,
Windows and MacOS as well.) The key program
needs both the iteration count and the seed as command line
options. You can cut-and-paste these right from the login prompt
on the machine that you are logging in to.
On the trusted system:
&prompt.user; key 97 fw13894
Reminder - Do not use this program while logged in via telnet or rlogin.
Enter secret password:
WELD LIP ACTS ENDS ME HAAG
Now that you have your one-time password you can continue
logging in:
login: <username>
s/key 97 fw13894
Password: <return to enable echo>
s/key 97 fw13894
Password [echo on]: WELD LIP ACTS ENDS ME HAAG
Last login: Tue Mar 21 11:56:41 from 10.0.0.2 ...
This is the easiest mechanism if you have
a trusted machine. There is a Java S/Key key
applet, The Java OTP
Calculator , that you can download and run locally on any
Java supporting browser.
Generating multiple one-time passwords
- Sometimes you have have to go places where you do not have
+ Sometimes you have to go places where you do not have
access to a trusted machine or secure connection. In this case,
it is possible to use the key command to
generate a number of one-time passwords before hand to be printed
out and taken with you. For example:
&prompt.user; key -n 5 30 zz99999
Reminder - Do not use this program while logged in via telnet or rlogin.
Enter secret password: <secret password>
26: SODA RUDE LEA LIND BUDD SILT
27: JILT SPY DUTY GLOW COWL ROT
28: THEM OW COLA RUNT BONG SCOT
29: COT MASH BARR BRIM NAN FLAG
30: CAN KNEE CAST NAME FOLK BILK
The -n 5 requests five keys in sequence, the
30 specifies what the last iteration number
should be. Note that these are printed out in
reverse order of eventual use. If you are
really paranoid, you might want to write the results down by hand;
otherwise you can cut-and-paste into lpr . Note
that each line shows both the iteration count and the one-time
password; you may still find it handy to scratch off passwords as
you use them.
Restricting use of UNIX passwords
Restrictions can be placed on the use of UNIX passwords based
on the host name, user name, terminal port, or IP address of a
login session. These restrictions can be found in the
configuration file /etc/skey.access . The
&man.skey.access.5; manual page has more info on the complete
format of the file and also details some security cautions to be
aware of before depending on this file for security.
If there is no /etc/skey.access file
(this is the FreeBSD default), then all users will be allowed to
use UNIX passwords. If the file exists, however, then all users
will be required to use S/Key unless explicitly permitted to do
otherwise by configuration statements in the
skey.access file. In all cases, UNIX
passwords are permitted on the console.
Here is a sample configuration file which illustrates the
three most common sorts of configuration statements:
permit internet 192.168.0.0 255.255.0.0
permit user fnord
permit port ttyd0
The first line (permit internet ) allows
users whose IP source address (which is vulnerable to spoofing)
matches the specified value and mask, to use UNIX passwords. This
should not be considered a security mechanism, but rather, a means
to remind authorized users that they are using an insecure network
and need to use S/Key for authentication.
The second line (permit user ) allows the
specified username, in this case fnord , to use
UNIX passwords at any time. Generally speaking, this should only
be used for people who are either unable to use the
key program, like those with dumb terminals, or
those who are uneducable.
The third line (permit port ) allows all
users logging in on the specified terminal line to use UNIX
passwords; this would be used for dial-ups.
Kerberos
Contributed by &a.markm; (based on contribution by
&a.md;).
Kerberos is a network add-on system/protocol that allows users to
authenticate themselves through the services of a secure server.
Services such as remote login, remote copy, secure inter-system file
copying and other high-risk tasks are made considerably safer and more
controllable.
The following instructions can be used as a guide on how to set up
Kerberos as distributed for FreeBSD. However, you should refer to the
relevant manual pages for a complete description.
In FreeBSD, the Kerberos is not that from the original 4.4BSD-Lite,
distribution, but eBones, which had been previously ported to FreeBSD
1.1.5.1, and was sourced from outside the USA/Canada, and was thus
available to system owners outside those countries during the era
of restrictive export controls on cryptographic code from the USA.
Creating the initial database
This is done on the Kerberos server only. First make sure that
you do not have any old Kerberos databases around. You should change
to the directory /etc/kerberosIV and check that
only the following files are present:
&prompt.root; cd /etc/kerberosIV
&prompt.root; ls
README krb.conf krb.realms
If any additional files (such as principal.*
or master_key ) exist, then use the
kdb_destroy command to destroy the old Kerberos
database, of if Kerberos is not running, simply delete the extra
files.
You should now edit the krb.conf and
krb.realms files to define your Kerberos realm.
In this case the realm will be GRONDAR.ZA and the
server is grunt.grondar.za . We edit or create
the krb.conf file:
&prompt.root; cat krb.conf
GRONDAR.ZA
GRONDAR.ZA grunt.grondar.za admin server
CS.BERKELEY.EDU okeeffe.berkeley.edu
ATHENA.MIT.EDU kerberos.mit.edu
ATHENA.MIT.EDU kerberos-1.mit.edu
ATHENA.MIT.EDU kerberos-2.mit.edu
ATHENA.MIT.EDU kerberos-3.mit.edu
LCS.MIT.EDU kerberos.lcs.mit.edu
TELECOM.MIT.EDU bitsy.mit.edu
ARC.NASA.GOV trident.arc.nasa.gov
In this case, the other realms do not need to be there. They are
here as an example of how a machine may be made aware of multiple
realms. You may wish to not include them for simplicity.
The first line names the realm in which this system works. The
other lines contain realm/host entries. The first item on a line is a
realm, and the second is a host in that realm that is acting as a
key distribution center
. The words admin
server following a hosts name means that host also
provides an administrative database server. For further explanation
of these terms, please consult the Kerberos man pages.
Now we have to add grunt.grondar.za
to the GRONDAR.ZA realm and also add an entry to
put all hosts in the .grondar.za
domain in the GRONDAR.ZA realm. The
krb.realms file would be updated as
follows:
&prompt.root; cat krb.realms
grunt.grondar.za GRONDAR.ZA
.grondar.za GRONDAR.ZA
.berkeley.edu CS.BERKELEY.EDU
.MIT.EDU ATHENA.MIT.EDU
.mit.edu ATHENA.MIT.EDU
Again, the other realms do not need to be there. They are here as
an example of how a machine may be made aware of multiple realms. You
may wish to remove them to simplify things.
The first line puts the specific system into
the named realm. The rest of the lines show how to default systems of
a particular subdomain to a named realm.
Now we are ready to create the database. This only needs to run
on the Kerberos server (or Key Distribution Center). Issue the
kdb_init command to do this:
&prompt.root; kdb_init
Realm name [default ATHENA.MIT.EDU ]: GRONDAR.ZA
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter Kerberos master key:
Now we have to save the key so that servers on the local machine
can pick it up. Use the kstash command to do
this.
&prompt.root; kstash
Enter Kerberos master key:
Current Kerberos master key version is 1.
Master key entered. BEWARE!
This saves the encrypted master password in
/etc/kerberosIV/master_key .
Making it all run
Two principals need to be added to the database for
each system that will be secured with Kerberos.
Their names are kpasswd and rcmd
These two principals are made for each system, with the instance being
the name of the individual system.
These daemons, kpasswd and
rcmd allow other systems to change Kerberos
passwords and run commands like rcp ,
rlogin and rsh .
Now let's add these entries:
&prompt.root; kdb_edit
Opening database...
Enter Kerberos master key:
Current Kerberos master key version is 1.
Master key entered. BEWARE!
Previous or default values are in [brackets] ,
enter return to leave the same, or new value.
Principal name: passwd
Instance: grunt
<Not found>, Create [y] ? y
Principal: passwd, Instance: grunt, kdc_key_ver: 1
New Password: <---- enter RANDOM here
Verifying password
New Password: <---- enter RANDOM here
Random password [y] ? y
Principal's new key version = 1
Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ?
Max ticket lifetime (*5 minutes) [ 255 ] ?
Attributes [ 0 ] ?
Edit O.K.
Principal name: rcmd
Instance: grunt
<Not found>, Create [y] ?
Principal: rcmd, Instance: grunt, kdc_key_ver: 1
New Password: <---- enter RANDOM here
Verifying password
New Password: <---- enter RANDOM here
Random password [y] ?
Principal's new key version = 1
Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ?
Max ticket lifetime (*5 minutes) [ 255 ] ?
Attributes [ 0 ] ?
Edit O.K.
Principal name: <---- null entry here will cause an exit
Creating the server file
We now have to extract all the instances which define the services
on each machine. For this we use the ext_srvtab
command. This will create a file which must be copied or moved
by secure means to each Kerberos client's
/etc/kerberosIV directory. This file must be present on each server
and client, and is crucial to the operation of Kerberos.
&prompt.root; ext_srvtab grunt
Enter Kerberos master key:
Current Kerberos master key version is 1.
Master key entered. BEWARE!
Generating 'grunt-new-srvtab'....
Now, this command only generates a temporary file which must be
renamed to srvtab so that all the server can pick
it up. Use the mv command to move it into place on
the original system:
&prompt.root; mv grunt-new-srvtab srvtab
If the file is for a client system, and the network is not deemed
safe, then copy the
client -new-srvtab to
removable media and transport it by secure physical means. Be sure to
rename it to srvtab in the client's
/etc/kerberosIV directory, and make sure it is
mode 600:
&prompt.root; mv grumble-new-srvtab srvtab
&prompt.root; chmod 600 srvtab
Populating the database
We now have to add some user entries into the database. First
let's create an entry for the user jane . Use the
kdb_edit command to do this:
&prompt.root; kdb_edit
Opening database...
Enter Kerberos master key:
Current Kerberos master key version is 1.
Master key entered. BEWARE!
Previous or default values are in [brackets] ,
enter return to leave the same, or new value.
Principal name: jane
Instance:
<Not found>, Create [y] ? y
Principal: jane, Instance: , kdc_key_ver: 1
New Password: <---- enter a secure password here
Verifying password
New Password: <---- re-enter the password here
Principal's new key version = 1
Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ?
Max ticket lifetime (*5 minutes) [ 255 ] ?
Attributes [ 0 ] ?
Edit O.K.
Principal name: <---- null entry here will cause an exit
Testing it all out
First we have to start the Kerberos daemons. NOTE that if you
have correctly edited your /etc/rc.conf then this
will happen automatically when you reboot. This is only necessary on
the Kerberos server. Kerberos clients will automagically get what
they need from the /etc/kerberosIV
directory.
&prompt.root; kerberos &
Kerberos server starting
Sleep forever on error
Log file is /var/log/kerberos.log
Current Kerberos master key version is 1.
Master key entered. BEWARE!
Current Kerberos master key version is 1
Local realm: GRONDAR.ZA
&prompt.root; kadmind -n &
KADM Server KADM0.0A initializing
Please do not use 'kill -9' to kill this job, use a
regular kill instead
Current Kerberos master key version is 1.
Master key entered. BEWARE!
Now we can try using the kinit command to get a
ticket for the id jane that we created
above:
&prompt.user; kinit jane
MIT Project Athena (grunt.grondar.za)
Kerberos Initialization for "jane"
Password:
Try listing the tokens using klist to see if we
really have them:
&prompt.user; klist
Ticket file: /tmp/tkt245
Principal: jane@GRONDAR.ZA
Issued Expires Principal
Apr 30 11:23:22 Apr 30 19:23:22 krbtgt.GRONDAR.ZA@GRONDAR.ZA
Now try changing the password using passwd to
check if the kpasswd daemon can get authorization to the Kerberos
database:
&prompt.user; passwd
realm GRONDAR.ZA
Old password for jane:
New Password for jane:
Verifying password
New Password for jane:
Password changed.
Adding su privileges
Kerberos allows us to give each user who
needs root privileges their own separate
su password. We could now add an id which is
authorized to su to root .
This is controlled by having an instance of root
associated with a principal. Using kdb_edit we can
create the entry jane.root in the Kerberos
database:
&prompt.root; kdb_edit
Opening database...
Enter Kerberos master key:
Current Kerberos master key version is 1.
Master key entered. BEWARE!
Previous or default values are in [brackets] ,
enter return to leave the same, or new value.
Principal name: jane
Instance: root
<Not found>, Create [y] ? y
Principal: jane, Instance: root, kdc_key_ver: 1
New Password: <---- enter a SECURE password here
Verifying password
New Password: <---- re-enter the password here
Principal's new key version = 1
Expiration date (enter yyyy-mm-dd) [ 2000-01-01 ] ?
Max ticket lifetime (*5 minutes) [ 255 ] ? 12 <--- Keep this short!
Attributes [ 0 ] ?
Edit O.K.
Principal name: <---- null entry here will cause an exit
Now try getting tokens for it to make sure it works:
&prompt.root; kinit jane.root
MIT Project Athena (grunt.grondar.za)
Kerberos Initialization for "jane.root"
Password:
Now we need to add the user to root's .klogin
file:
&prompt.root; cat /root/.klogin
jane.root@GRONDAR.ZA
Now try doing the su :
&prompt.user; su
Password:
and take a look at what tokens we have:
&prompt.root; klist
Ticket file: /tmp/tkt_root_245
Principal: jane.root@GRONDAR.ZA
Issued Expires Principal
May 2 20:43:12 May 3 04:43:12 krbtgt.GRONDAR.ZA@GRONDAR.ZA
Using other commands
In an earlier example, we created a principal called
jane with an instance root .
This was based on a user with the same name as the principal, and this
is a Kerberos default; that a
<principal>.<instance> of the form
<username>. root will allow
that <username> to su to
root if the necessary entries are in the .klogin
file in root 's home directory:
&prompt.root; cat /root/.klogin
jane.root@GRONDAR.ZA
Likewise, if a user has in their own home directory lines of the
form:
&prompt.user; cat ~/.klogin
jane@GRONDAR.ZA
jack@GRONDAR.ZA
This allows anyone in the GRONDAR.ZA realm
who has authenticated themselves to jane or
jack (via kinit , see above)
access to rlogin to jane 's
account or files on this system (grunt ) via
rlogin , rsh or
rcp .
For example, Jane now logs into another system, using
Kerberos:
&prompt.user; kinit
MIT Project Athena (grunt.grondar.za)
Password:
%prompt.user; rlogin grunt
Last login: Mon May 1 21:14:47 from grumble
Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD BUILT-19950429 (GR386) #0: Sat Apr 29 17:50:09 SAT 1995
Or Jack logs into Jane's account on the same machine (Jane having
set up the .klogin file as above, and the person
in charge of Kerberos having set up principal
jack with a null instance:
&prompt.user; kinit
&prompt.user; rlogin grunt -l jane
MIT Project Athena (grunt.grondar.za)
Password:
Last login: Mon May 1 21:16:55 from grumble
Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD BUILT-19950429 (GR386) #0: Sat Apr 29 17:50:09 SAT 1995
Firewalls
Contributed by &a.gpalmer; and Alex Nash.
Firewalls are an area of increasing interest for people who are
connected to the Internet, and are even finding applications on private
networks to provide enhanced security. This section will hopefully
explain what firewalls are, how to use them, and how to use the
facilities provided in the FreeBSD kernel to implement them.
People often think that having a firewall between your
internal network and the Big Bad Internet
will solve all
your security problems. It may help, but a poorly setup firewall
system is more of a security risk than not having one at all. A
firewall can add another layer of security to your systems, but it
cannot stop a really determined cracker from penetrating your internal
network. If you let internal security lapse because you believe your
firewall to be impenetrable, you have just made the crackers job that
much easier.
What is a firewall?
There are currently two distinct types of firewalls in common use
on the Internet today. The first type is more properly called a
packet filtering router , where the kernel on a
multi-homed machine chooses whether to forward or block packets based
on a set of rules. The second type, known as a proxy
server , relies on daemons to provide authentication and to
forward packets, possibly on a multi-homed machine which has kernel
packet forwarding disabled.
Sometimes sites combine the two types of firewalls, so that only a
certain machine (known as a bastion host ) is
allowed to send packets through a packet filtering router onto an
internal network. Proxy services are run on the bastion host, which
are generally more secure than normal authentication
mechanisms.
FreeBSD comes with a kernel packet filter (known as
IPFW ), which is what the rest of this
section will concentrate on. Proxy servers can be built on FreeBSD
from third party software, but there is such a variety of proxy
servers available that it would be impossible to cover them in this
document.
Packet filtering routers
A router is a machine which forwards packets between two or more
networks. A packet filtering router has an extra piece of code in
its kernel which compares each packet to a list of rules before
deciding if it should be forwarded or not. Most modern IP routing
software has packet filtering code within it that defaults to
forwarding all packets. To enable the filters, you need to define a
set of rules for the filtering code so it can decide if the
packet should be allowed to pass or not.
To decide whether a packet should be passed on, the code looks
through its set of rules for a rule which matches the contents of
this packets headers. Once a match is found, the rule action is
obeyed. The rule action could be to drop the packet, to forward the
packet, or even to send an ICMP message back to the originator.
Only the first match counts, as the rules are searched in order.
Hence, the list of rules can be referred to as a rule
chain
.
The packet matching criteria varies depending on the software
used, but typically you can specify rules which depend on the source
IP address of the packet, the destination IP address, the source
port number, the destination port number (for protocols which
support ports), or even the packet type (UDP, TCP, ICMP,
etc).
Proxy servers
Proxy servers are machines which have had the normal system
daemons (telnetd, ftpd, etc) replaced with special servers. These
servers are called proxy servers as they
normally only allow onward connections to be made. This enables you
to run (for example) a proxy telnet server on your firewall host,
and people can telnet in to your firewall from the outside, go
through some authentication mechanism, and then gain access to the
internal network (alternatively, proxy servers can be used for
signals coming from the internal network and heading out).
Proxy servers are normally more secure than normal servers, and
often have a wider variety of authentication mechanisms available,
including one-shot
password systems so that even if
someone manages to discover what password you used, they will not be
able to use it to gain access to your systems as the password
instantly expires. As they do not actually give users access to the
host machine, it becomes a lot more difficult for someone to install
backdoors around your security system.
Proxy servers often have ways of restricting access further, so
that only certain hosts can gain access to the servers, and often
they can be set up so that you can limit which users can talk to
which destination machine. Again, what facilities are available
depends largely on what proxy software you choose.
What does IPFW allow me to do?
IPFW , the software supplied with
FreeBSD, is a packet filtering and accounting system which resides in
the kernel, and has a user-land control utility,
&man.ipfw.8;. Together, they allow you to define and query the
rules currently used by the kernel in its routing decisions.
There are two related parts to IPFW .
The firewall section allows you to perform packet filtering. There is
also an IP accounting section which allows you to track usage of your
router, based on similar rules to the firewall section. This allows
you to see (for example) how much traffic your router is getting from
a certain machine, or how much WWW (World Wide Web) traffic it is
forwarding.
As a result of the way that IPFW is
designed, you can use IPFW on non-router
machines to perform packet filtering on incoming and outgoing
connections. This is a special case of the more general use of
IPFW , and the same commands and techniques
should be used in this situation.
Enabling IPFW on FreeBSD
As the main part of the IPFW system
lives in the kernel, you will need to add one or more options to your
kernel configuration file, depending on what facilities you want, and
recompile your kernel. See reconfiguring
the kernel for more details on how to recompile your
kernel.
There are currently three kernel configuration options relevant to
IPFW:
options IPFIREWALL
Compiles into the kernel the code for packet
filtering.
options IPFIREWALL_VERBOSE
Enables code to allow logging of packets through
&man.syslogd.8;. Without this option, even if you specify
that packets should be logged in the filter rules, nothing will
happen.
options IPFIREWALL_VERBOSE_LIMIT=10
Limits the number of packets logged through
&man.syslogd.8; on a per entry basis. You may wish to use
this option in hostile environments in which you want to log
firewall activity, but do not want to be open to a denial of
service attack via syslog flooding.
When a chain entry reaches the packet limit specified,
logging is turned off for that particular entry. To resume
logging, you will need to reset the associated counter using the
&man.ipfw.8; utility:
&prompt.root; ipfw zero 4500
Where 4500 is the chain entry you wish to continue
logging.
Previous versions of FreeBSD contained an
IPFIREWALL_ACCT option. This is now obsolete as
the firewall code automatically includes accounting
facilities.
Configuring IPFW
The configuration of the IPFW software
is done through the &man.ipfw.8; utility. The syntax for this
command looks quite complicated, but it is relatively simple once you
understand its structure.
There are currently four different command categories used by the
utility: addition/deletion, listing, flushing, and clearing.
Addition/deletion is used to build the rules that control how packets
are accepted, rejected, and logged. Listing is used to examine the
contents of your rule set (otherwise known as the chain) and packet
counters (accounting). Flushing is used to remove all entries from
the chain. Clearing is used to zero out one or more accounting
entries.
Altering the IPFW rules
The syntax for this form of the command is:
ipfw
-N
command
index
action
log
protocol
addresses
options
There is one valid flag when using this form of the
command:
-N
Resolve addresses and service names in output.
The command given can be shortened to the
shortest unique form. The valid commands
are:
add
Add an entry to the firewall/accounting rule list
delete
Delete an entry from the firewall/accounting rule
list
Previous versions of IPFW used
separate firewall and accounting entries. The present version
provides packet accounting with each firewall entry.
If an index value is supplied, it used to
place the entry at a specific point in the chain. Otherwise, the
entry is placed at the end of the chain at an index 100 greater than
the last chain entry (this does not include the default policy, rule
65535, deny).
The log option causes matching rules to be
output to the system console if the kernel was compiled with
IPFIREWALL_VERBOSE .
Valid actions are:
reject
Drop the packet, and send an ICMP host or port unreachable
(as appropriate) packet to the source.
allow
Pass the packet on as normal. (aliases:
pass and
accept )
deny
Drop the packet. The source is not notified via an
ICMP message (thus it appears that the packet never
arrived at the destination).
count
Update packet counters but do not allow/deny the packet
based on this rule. The search continues with the next chain
entry.
Each action will be recognized by the
shortest unambiguous prefix.
The protocols which can be specified
are:
all
Matches any IP packet
icmp
Matches ICMP packets
tcp
Matches TCP packets
udp
Matches UDP packets
The address specification is:
from
address/mask port
to
address/mask port
via interface
You can only specify port in
conjunction with protocols which support ports
(UDP and TCP).
The via is optional and may specify the IP
address or domain name of a local IP interface, or an interface name
(e.g. ed0 ) to match only packets coming
through this interface. Interface unit numbers can be specified
with an optional wildcard. For example, ppp*
would match all kernel PPP interfaces.
The syntax used to specify an
address/mask is:
address
or
address /mask-bits
or
address :mask-pattern
A valid hostname may be specified in place of the IP address.
mask-bits is a decimal
number representing how many bits in the address mask should be set.
e.g. specifying 192.216.222.1/24 will create a
mask which will allow any address in a class C subnet (in this case,
192.216.222) to be matched.
mask-pattern is an IP
address which will be logically AND'ed with the address given. The
keyword any may be used to specify any IP
address
.
The port numbers to be blocked are specified as:
port ,port ,port …
to specify either a single port or a list of ports, or
port -port
to specify a range of ports. You may also combine a single range
with a list, but the range must always be specified first.
The options available are:
frag
Matches if the packet is not the first fragment of the
datagram.
in
Matches if the packet is on the way in.
out
Matches if the packet is on the way out.
ipoptions spec
Matches if the IP header contains the comma separated list
of options specified in spec . The
supported list of IP options are: ssrr
(strict source route), lsrr (loose source
route), rr (record packet route), and
ts (time stamp). The absence of a
particular option may be denoted with a leading
! .
established
Matches if the packet is part of an already established
TCP connection (i.e. it has the RST or ACK bits set). You can
optimize the performance of the firewall by placing
established rules early in the
chain.
setup
Matches if the packet is an attempt to establish a TCP
connection (the SYN bit set is set but the ACK bit is
not).
tcpflags flags
Matches if the TCP header contains the comma separated
list of flags . The supported flags
are fin , syn ,
rst , psh ,
ack , and urg . The
absence of a particular flag may be indicated by a leading
! .
icmptypes types
Matches if the ICMP type is present in the list
types . The list may be specified
as any combination of ranges and/or individual types separated
by commas. Commonly used ICMP types are: 0
echo reply (ping reply), 3 destination
unreachable, 5 redirect,
8 echo request (ping request), and
11 time exceeded (used to indicate TTL
expiration as with &man.traceroute.8;).
Listing the IPFW rules
The syntax for this form of the command is:
ipfw
-a
-t
-N
l
There are three valid flags when using this form of the
command:
-a
While listing, show counter values. This option is the
only way to see accounting counters.
-t
Display the last match times for each chain entry. The
time listing is incompatible with the input syntax used by the
&man.ipfw.8; utility.
-N
Attempt to resolve given addresses and service
names.
Flushing the IPFW rules
The syntax for flushing the chain is:
ipfw
flush
This causes all entries in the firewall chain to be removed
except the fixed default policy enforced by the kernel (index
65535). Use caution when flushing rules, the default deny policy
will leave your system cut off from the network until allow entries
are added to the chain.
Clearing the IPFW packet counters
The syntax for clearing one or more packet counters is:
ipfw
zero
index
When used without an index argument,
all packet counters are cleared. If an
index is supplied, the clearing operation
only affects a specific chain entry.
Example commands for ipfw
This command will deny all packets from the host evil.crackers.org to the telnet port of the
host nice.people.org :
&prompt.root ipfw add deny tcp from evil.crackers.org to nice.people.org 23
The next example denies and logs any TCP traffic from the entire
crackers.org network (a class C) to
the nice.people.org machine (any
port).
&prompt.root; ipfw add deny log tcp from evil.crackers.org/24 to nice.people.org
If you do not want people sending X sessions to your internal
network (a subnet of a class C), the following command will do the
necessary filtering:
&prompt.root; ipfw add deny tcp from any to my.org/28 6000 setup
To see the accounting records:
&prompt.root; ipfw -a list
or in the short form
&prompt.root; ipfw -a l
You can also see the last time a chain entry was matched
with:
&prompt.root; ipfw -at l
Building a packet filtering firewall
The following suggestions are just that: suggestions. The
requirements of each firewall are different and we cannot tell you
how to build a firewall to meet your particular requirements.
When initially setting up your firewall, unless you have a test
bench setup where you can configure your firewall host in a controlled
environment, it is strongly recommend you use the logging version of the
commands and enable logging in the kernel. This will allow you to
quickly identify problem areas and cure them without too much
disruption. Even after the initial setup phase is complete, I
recommend using the logging for `deny' as it allows tracing of
possible attacks and also modification of the firewall rules if your
requirements alter.
If you use the logging versions of the accept
command, it can generate large amounts of log
data as one log line will be generated for every packet that passes
through the firewall, so large ftp/http transfers, etc, will really
slow the system down. It also increases the latencies on those
packets as it requires more work to be done by the kernel before the
packet can be passed on. syslogd with also start using up a lot
more processor time as it logs all the extra data to disk, and it
could quite easily fill the partition /var/log
is located on.
You should enable your firewall from
/etc/rc.conf.local or
/etc/rc.conf . The associated man page explains
which knobs to fiddle and lists some preset firewall configurations.
If you do not use a preset configuration, ipfw list
will output the current ruleset into a file that you can
pass to rc.conf . If you do not use
/etc/rc.conf.local or
/etc/rc.conf to enable your firewall,
it is important to make sure your firewall is enabled before
any IP interfaces are configured.
The next problem is what your firewall should actually
do ! This is largely dependent on what access to
your network you want to allow from the outside, and how much access
to the outside world you want to allow from the inside. Some general
rules are:
Block all incoming access to ports below 1024 for TCP. This is
where most of the security sensitive services are, like finger,
SMTP (mail) and telnet.
Block all incoming UDP traffic. There
are very few useful services that travel over UDP, and what useful
- traffic there is is normally a security threat (e.g. Suns RPC and
+ traffic there is normally a security threat (e.g. Suns RPC and
NFS protocols). This has its disadvantages also, since UDP is a
connectionless protocol, denying incoming UDP traffic also blocks
the replies to outgoing UDP traffic. This can cause a problem for
people (on the inside) using external archie (prospero) servers.
If you want to allow access to archie, you'll have to allow
packets coming from ports 191 and 1525 to any internal UDP port
through the firewall. ntp is another service you may consider
allowing through, which comes from port 123.
Block traffic to port 6000 from the outside. Port 6000 is the
port used for access to X11 servers, and can be a security threat
(especially if people are in the habit of doing xhost
+ on their workstations). X11 can actually use a
range of ports starting at 6000, the upper limit being how many X
displays you can run on the machine. The upper limit as defined
by RFC 1700 (Assigned Numbers) is 6063.
Check what ports any internal servers use (e.g. SQL servers,
etc). It is probably a good idea to block those as well, as they
normally fall outside the 1-1024 range specified above.
Another checklist for firewall configuration is available from
CERT at http://www.cert.org/tech_tips/packet_filtering.html
As stated above, these are only guidelines .
You will have to decide what filter rules you want to use on your
firewall yourself. We cannot accept ANY responsibility if someone
breaks into your network, even if you follow the advice given
above.
OpenSSL
As of FreeBSD 4.0, the OpenSSL toolkit is a part of the base
system. OpenSSL
provides a general-purpose cryptography library, as well as the
Secure Sockets Layer v2/v3 (SSLv2/SSLv3) and Transport Layer
Security v1 (TLSv1) network security protocols.
However, one of the algorithms (specifically IDEA)
included in OpenSSL is protected by patents in the USA and
elsewhere, and is not available for unrestricted use.
IDEA is included in the OpenSSL sources in FreeBSD, but it is not
built by default. If you wish to use it, and you comply with the
license terms, enable the MAKE_IDEA switch in /etc/make.conf and
rebuild your sources using 'make world'.
Today, the RSA algorithm is free for use in USA and other
countries. In the past it was protected by a patent.
Source Code Installations
OpenSSL is part of the src-crypto and
src-secure cvsup collections. See the Obtaining FreeBSD section for more
information about obtaining and updating FreeBSD source
code.
IPsec
Contributed by &a.shin;, 5 March
2000.
The IPsec mechanism provides secure communication either for IP
layer and socket layer communication. This section should
explain how to use them. For implementation details, please
refer to The
Developers' Handbook .
The current IPsec implementation supports both transport mode
and tunnel mode. However, tunnel mode comes with some restrictions.
http://www.kame.net/newsletter/
has more comprehensive examples.
Please be aware that in order to use this functionality, you
must have the following options compiled into your kernel:
options IPSEC #IP security
options IPSEC_ESP #IP security (crypto; define w/IPSEC)
Transport mode example with IPv4
Let's setup security association to deploy a secure channel
between HOST A (10.2.3.4) and HOST B (10.6.7.8). Here we show a little
complicated example. From HOST A to HOST B, only old AH is used.
From HOST B to HOST A, new AH and new ESP are combined.
Now we should choose algorithm to be used corresponding to
"AH"/"new AH"/"ESP"/"new ESP". Please refer to the &man.setkey.8; man
page to know algorithm names. Our choice is MD5 for AH, new-HMAC-SHA1
for new AH, and new-DES-expIV with 8 byte IV for new ESP.
Key length highly depends on each algorithm. For example, key
length must be equal to 16 bytes for MD5, 20 for new-HMAC-SHA1,
and 8 for new-DES-expIV. Now we choose "MYSECRETMYSECRET",
"KAMEKAMEKAMEKAMEKAME", "PASSWORD", respectively.
OK, let's assign SPI (Security Parameter Index) for each protocol.
Please note that we need 3 SPIs for this secure channel since three
security headers are produced (one for from HOST A to HOST B, two for
from HOST B to HOST A). Please also note that SPI MUST be greater
than or equal to 256. We choose, 1000, 2000, and 3000, respectively.
(1)
HOST A ------> HOST B
(1)PROTO=AH
ALG=MD5(RFC1826)
KEY=MYSECRETMYSECRET
SPI=1000
(2.1)
HOST A <------ HOST B
<------
(2.2)
(2.1)
PROTO=AH
ALG=new-HMAC-SHA1(new AH)
KEY=KAMEKAMEKAMEKAMEKAME
SPI=2000
(2.2)
PROTO=ESP
ALG=new-DES-expIV(new ESP)
IV length = 8
KEY=PASSWORD
SPI=3000
Now, let's setup security association. Execute &man.setkey.8;
on both HOST A and B:
&prompt.root; setkey -c
add 10.2.3.4 10.6.7.8 ah-old 1000 -m transport -A keyed-md5 "MYSECRETMYSECRET" ;
add 10.6.7.8 10.2.3.4 ah 2000 -m transport -A hmac-sha1 "KAMEKAMEKAMEKAMEKAME" ;
add 10.6.7.8 10.2.3.4 esp 3000 -m transport -E des-cbc "PASSWORD" ;
^D
Actually, IPsec communication doesn't process until security policy
entries will be defined. In this case, you must setup each host.
At A:
&prompt.root; setkey -c
spdadd 10.2.3.4 10.6.7.8 any -P out ipsec
ah/transport/10.2.3.4-10.6.7.8/require ;
^D
At B:
&prompt.root; setkey -c
spdadd 10.6.7.8 10.2.3.4 any -P out ipsec
esp/transport/10.6.7.8-10.2.3.4/require ;
spdadd 10.6.7.8 10.2.3.4 any -P out ipsec
ah/transport/10.6.7.8-10.2.3.4/require ;
^D
HOST A --------------------------------------> HOST E
10.2.3.4 10.6.7.8
| |
========== old AH keyed-md5 ==========>
<========= new AH hmac-sha1 ===========
<========= new ESP des-cbc ============
Transport mode example with IPv6
Another example using IPv6.
ESP transport mode is recommended for TCP port number 110 between
Host-A and Host-B.
============ ESP ============
| |
Host-A Host-B
fec0::10 -------------------- fec0::11
Encryption algorithm is blowfish-cbc whose key is "kamekame", and
authentication algorithm is hmac-sha1 whose key is "this is the test
key". Configuration at Host-A:
&prompt.root; setkey -c <<EOF
spdadd fec0::10[any] fec0::11[110] tcp -P out ipsec
esp/transport/fec0::10-fec0::11/use ;
spdadd fec0::11[110] fec0::10[any] tcp -P in ipsec
esp/transport/fec0::11-fec0::10/use ;
add fec0::10 fec0::11 esp 0x10001
-m transport
-E blowfish-cbc "kamekame"
-A hmac-sha1 "this is the test key" ;
add fec0::11 fec0::10 esp 0x10002
-m transport
-E blowfish-cbc "kamekame"
-A hmac-sha1 "this is the test key" ;
EOF
and at Host-B:
&prompt.root; setkey -c <<EOF
spdadd fec0::11[110] fec0::10[any] tcp -P out ipsec
esp/transport/fec0::11-fec0::10/use ;
spdadd fec0::10[any] fec0::11[110] tcp -P in ipsec
esp/transport/fec0::10-fec0::11/use ;
add fec0::10 fec0::11 esp 0x10001 -m transport
-E blowfish-cbc "kamekame"
-A hmac-sha1 "this is the test key" ;
add fec0::11 fec0::10 esp 0x10002 -m transport
-E blowfish-cbc "kamekame"
-A hmac-sha1 "this is the test key" ;
EOF
Note the direction of SP.
Tunnel mode example with IPv4
Tunnel mode between two security gateways
Security protocol is old AH tunnel mode, i.e. specified by
RFC1826, with keyed-md5 whose key is "this is the test" as
authentication algorithm.
======= AH =======
| |
Network-A Gateway-A Gateway-B Network-B
10.0.1.0/24 ---- 172.16.0.1 ----- 172.16.0.2 ---- 10.0.2.0/24
Configuration at Gateway-A:
&prompt.root; setkey -c <<EOF
spdadd 10.0.1.0/24 10.0.2.0/24 any -P out ipsec
ah/tunnel/172.16.0.1-172.16.0.2/require ;
spdadd 10.0.2.0/24 10.0.1.0/24 any -P in ipsec
ah/tunnel/172.16.0.2-172.16.0.1/require ;
add 172.16.0.1 172.16.0.2 ah-old 0x10003 -m any
-A keyed-md5 "this is the test" ;
add 172.16.0.2 172.16.0.1 ah-old 0x10004 -m any
-A keyed-md5 "this is the test" ;
EOF
If port number field is omitted such above then "[any]" is
employed. `-m' specifies the mode of SA to be used. "-m any" means
wild-card of mode of security protocol. You can use this SA for both
tunnel and transport mode.
and at Gateway-B:
&prompt.root; setkey -c <<EOF
spdadd 10.0.2.0/24 10.0.1.0/24 any -P out ipsec
ah/tunnel/172.16.0.2-172.16.0.1/require ;
spdadd 10.0.1.0/24 10.0.2.0/24 any -P in ipsec
ah/tunnel/172.16.0.1-172.16.0.2/require ;
add 172.16.0.1 172.16.0.2 ah-old 0x10003 -m any
-A keyed-md5 "this is the test" ;
add 172.16.0.2 172.16.0.1 ah-old 0x10004 -m any
-A keyed-md5 "this is the test" ;
EOF
Making SA bundle between two security gateways
AH transport mode and ESP tunnel mode is required between
Gateway-A and Gateway-B. In this case, ESP tunnel mode is applied first,
and AH transport mode is next.
========== AH =========
| ======= ESP ===== |
| | | |
Network-A Gateway-A Gateway-B Network-B
fec0:0:0:1::/64 --- fec0:0:0:1::1 ---- fec0:0:0:2::1 --- fec0:0:0:2::/64
Tunnel mode example with IPv6
Encryption algorithm is 3des-cbc, and authentication algorithm
for ESP is hmac-sha1. Authentication algorithm for AH is hmac-md5.
Configuration at Gateway-A:
&prompt.root; setkey -c <<EOF
spdadd fec0:0:0:1::/64 fec0:0:0:2::/64 any -P out ipsec
esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require
ah/transport/fec0:0:0:1::1-fec0:0:0:2::1/require ;
spdadd fec0:0:0:2::/64 fec0:0:0:1::/64 any -P in ipsec
esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require
ah/transport/fec0:0:0:2::1-fec0:0:0:1::1/require ;
add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10001 -m tunnel
-E 3des-cbc "kamekame12341234kame1234"
-A hmac-sha1 "this is the test key" ;
add fec0:0:0:1::1 fec0:0:0:2::1 ah 0x10001 -m transport
-A hmac-md5 "this is the test" ;
add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10001 -m tunnel
-E 3des-cbc "kamekame12341234kame1234"
-A hmac-sha1 "this is the test key" ;
add fec0:0:0:2::1 fec0:0:0:1::1 ah 0x10001 -m transport
-A hmac-md5 "this is the test" ;
EOF
Making SAs with the different end
ESP tunnel mode is required between Host-A and Gateway-A. Encryption
algorithm is cast128-cbc, and authentication algorithm for ESP is
hmac-sha1. ESP transport mode is recommended between Host-A and Host-B.
Encryption algorithm is rc5-cbc, and authentication algorithm for ESP is
hmac-md5.
================== ESP =================
| ======= ESP ======= |
| | | |
Host-A Gateway-A Host-B
fec0:0:0:1::1 ---- fec0:0:0:2::1 ---- fec0:0:0:2::2
Configuration at Host-A:
&prompt.root; setkey -c <<EOF
spdadd fec0:0:0:1::1[any] fec0:0:0:2::2[80] tcp -P out ipsec
esp/transport/fec0:0:0:1::1-fec0:0:0:2::2/use
esp/tunnel/fec0:0:0:1::1-fec0:0:0:2::1/require ;
spdadd fec0:0:0:2::1[80] fec0:0:0:1::1[any] tcp -P in ipsec
esp/transport/fec0:0:0:2::2-fec0:0:0:l::1/use
esp/tunnel/fec0:0:0:2::1-fec0:0:0:1::1/require ;
add fec0:0:0:1::1 fec0:0:0:2::2 esp 0x10001
-m transport
-E cast128-cbc "12341234"
-A hmac-sha1 "this is the test key" ;
add fec0:0:0:1::1 fec0:0:0:2::1 esp 0x10002
-E rc5-cbc "kamekame"
-A hmac-md5 "this is the test" ;
add fec0:0:0:2::2 fec0:0:0:1::1 esp 0x10003
-m transport
-E cast128-cbc "12341234"
-A hmac-sha1 "this is the test key" ;
add fec0:0:0:2::1 fec0:0:0:1::1 esp 0x10004
-E rc5-cbc "kamekame"
-A hmac-md5 "this is the test" ;
EOF
OpenSSH
Contributed by &a.chern;, April 21,
2001.
Secure shell is a set of network connectivity tools used to
access remote machines securely. It can be used as a direct
replacement for rlogin ,
rsh , rcp , and
telnet . Additionally, any other TCP/IP
connections can be tunneled/forwarded securely through ssh.
ssh encrypts all traffic to effectively eliminate eavesdropping,
connection hijacking, and other network-level attacks.
OpenSSH is maintained by the OpenBSD project, and is based
upon SSH v1.2.12 with all the recent bug fixes and updates. It
is compatible with both SSH protocols 1 and 2. OpenSSH has been
in the base system since FreeBSD 4.0.
Advantages of using OpenSSH
Normally, when using &man.telnet.1; or &man.rlogin.1;,
data is sent over the network in an clear, un-encrypted form.
Network sniffers anywhere in between the client and server can
steal your user/password information or data transferred in
your session. OpenSSH offers a variety of authentication and
encryption methods to prevent this from happening.
Enabling sshd
Be sure to make the following additions to your
rc.conf file:
sshd_enable="YES"
This will load the ssh daemon the next time your system
initializes. Alternatively, you can simply run the
sshd daemon.
SSH client
The &man.ssh.1; utility works similarly to
&man.rlogin.1;.
&prompt.root ssh user@foobardomain.com
Host key not found from the list of known hosts.
Are you sure you want to continue connecting (yes/no)? yes
Host 'foobardomain.com' added to the list of known hosts.
user@foobardomain.com's password: *******
The login will continue just as it would have if a session was
created using rlogin or telnet. SSH utilizes a key fingerprint
system for verifying the authenticity of the server when the
client connects. The user is prompted to enter 'yes' only during
the first time connecting. Future attempts to login are all
verified against the saved fingerprint key. The SSH client
will alert you if the saved fingerprint differs from the
received fingerprint on future login attempts. The fingerprints
are saved in ~/.ssh/known_hosts
Secure copy
The scp command works similarly to rcp; it copies a
file to or from a remote machine, except in a secure fashion.
&prompt.root scp user@foobardomain.com:/COPYRIGHT COPYRIGHT
user@foobardomain.com's password:
COPYRIGHT 100% |*****************************| 4735
00:00
&prompt.root
Since the fingerprint was already saved for this host in the
previous example, it is verified when using scp
here.
Configuration
The system-wide configuration files for both the OpenSSH
daemon and client reside within the /etc/ssh
directory.
ssh_config configures the client
settings, while sshd_config configures the
daemon.
ssh-keygen
Instead of using passwords, &man.ssh-keygen.1; can
be used to generate RSA keys to authenticate a user.
&prompt.user ssh-keygen
Initializing random number generator...
Generating p: .++ (distance 66)
Generating q: ..............................++ (distance 498)
Computing the keys...
Key generation complete.
Enter file in which to save the key (/home/user/.ssh/identity):
Enter passphrase:
Enter the same passphrase again:
Your identification has been saved in /home/user/.ssh/identity.
...
&man.ssh-keygen.1; will create a public and private
key pair for use in authentication. The private key is stored in
~/.ssh/identity , whereas the public key is
stored in ~/.ssh/identity.pub . The public
key must be placed in ~/.ssh/authorized_keys
of the remote machine in order for the setup to work.
This will allow connection to the remote machine based upon
RSA authentication instead of passwords.
If a passphrase is used in &man.ssh-keygen.1;, the user
will be prompted for a password each time in order to use the private
key.
&man.ssh-agent.1; and &man.ssh-add.1; are
utilities used in managing multiple passworded private keys.
SSH Tunneling
OpenSSH has the ability to create a tunnel to encapsulate
another protocol in an encrypted session.
The following command tells &man.ssh.1; to create a tunnel
for telnet.
&prompt.user; ssh -2 -N -f -L 5023:localhost:23 user@foo.bar.com
&prompt.user;
-2 this forces &man.ssh.1 to use version
2 of the protocol. (Do not use if you are working with older ssh
servers)
-N indicates no command, or tunnel only.
If omitted, &man.ssh.1; would initiate a normal session.
-f forces &man.ssh.1; to run
in the background.
-L indicates a local tunnel in
localport:localhost:remoteport fashion.
foo.bar.com is the remote/target
SSH server.
An SSH tunnel works by creating a listen socket on the specified
local host and port. It then forwards any connection to the local
host/port via the SSH connection to the remote machine on the
specified remote port.
In the example, port 5023 on localhost
is being forwarded to port 23 on the remote
machine. Since 23 is telnet, this would
create a secure telnet session through an SSH tunnel.
This can be used to wrap any number of insecure TCP protocols
such as smtp, pop3, ftp, etc.
A typical SSH Tunnel
&prompt.user; ssh -2 -N -f -L 5025:localhost:25 user@mailserver.foobar.com
user@mailserver.foobar.com's password: *****
&prompt.user; telnet localhost 5025
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 mailserver.foobar.com ESMTP
This can be used in conjunction with an &man.ssh-keygen.1;
and additional user accounts to create a more seamless/hassle-free
SSH tunneling environment. Keys can be used in place of typing
a password, and the tunnels can be run as a separate user.
Further Reading
OpenSSH
&man.ssh.1; &man.scp.1; &man.ssh-keygen.1;
&man.ssh-agent.1; &man.ssh-add.1;
&man.sshd.8; &man.sftp-server.8;
diff --git a/en_US.ISO8859-1/books/porters-handbook/book.sgml b/en_US.ISO8859-1/books/porters-handbook/book.sgml
index aa6c3ed589..63e3486280 100644
--- a/en_US.ISO8859-1/books/porters-handbook/book.sgml
+++ b/en_US.ISO8859-1/books/porters-handbook/book.sgml
@@ -1,4498 +1,4499 @@
%man;
%bookinfo;
%authors;
%mailing-lists;
]>
FreeBSD Porter's Handbook
The FreeBSD Documentation Project
April 2000
2000
The FreeBSD Documentation
Project
&bookinfo.legalnotice;
Making a port yourself
So, now you are interested in making your own port or
upgrading an existing one? Great!
What follows are some guidelines for creating a new port for
FreeBSD. If you want to upgrade an existing port, you should
read this and then read .
When this document is not sufficiently detailed, you should
refer to /usr/ports/Mk/bsd.port.mk , which
all port Makefiles include. Even if you do not hack Makefiles
daily, it is well commented, and you will still gain much
knowledge from it. Additionally, you may send specific questions
to the &a.ports;.
Only a fraction of the variables
(VAR ) that can be
overridden are mentioned in this document. Most (if not all)
are documented at the start of bsd.port.mk .
This file uses a non-standard tab setting.
Emacs and
Vim should recognize the setting on
loading the file. Both vi and
ex can be set to use the correct value by
typing :set tabstop=4 once the file has been
loaded.
Quick Porting
This section tells you how to do a quick port. In many cases, it
is not enough, but we will see.
First, get the original tarball and put it into
DISTDIR , which defaults to
/usr/ports/distfiles .
The following assumes that the software compiled out-of-the-box,
i.e., there was absolutely no change required for the port to work
on your FreeBSD box. If you needed to change something, you will
have to refer to the next section too.
Writing the Makefile
The minimal Makefile would look something
like this:
# New ports collection makefile for: oneko
# Date created: 5 December 1994
# Whom: asami
#
# $FreeBSD$
#
PORTNAME= oneko
PORTVERSION= 1.1b
CATEGORIES= games
MASTER_SITES= ftp://ftp.cs.columbia.edu/archives/X11R5/contrib/
MAINTAINER= asami@FreeBSD.org
MAN1= oneko.1
MANCOMPRESSED= yes
USE_IMAKE= yes
.include <bsd.port.mk>
See if you can figure it out. Do not worry about the contents
of the $FreeBSD$ line, it will be
filled in automatically by CVS when the port is imported to our main
ports tree. You can find a more detailed example in the sample Makefile section.
Writing the description files
There are three description files that are required for
any port, whether they actually package or not. They are
pkg-comment ,
pkg-descr , and
pkg-plist , and their
pkg- prefix distinguishes them from
other files.
pkg-comment
This is the one-line description of the port.
Please do not include the package name (or
version number of the software) in the comment. The comment
should begin with a capital, and end without a period. Here
is an example:
A cat chasing a mouse all over the screen
pkg-descr
This is a longer description of the port. One to a few
paragraphs concisely explaining what the port does is
sufficient.
This is not a manual or an in-depth
description on how to use or compile the port! Please
be careful if you are copying from the
README or manpage ; too often
they are not a concise description of the port or are in an
awkward format (e.g., manpages have justified spacing). If the
ported software has an official WWW homepage, you should list it
here. Prefix one of the websites with
WWW: so that automated tools will work
correctly.
It is recommended that you sign your name at the end of this
file, as in:
This is a port of oneko, in which a cat chases a poor mouse all over
the screen.
:
(etc.)
WWW: http://www.oneko.org/
- Satoshi
asami@cs.berkeley.edu
pkg-plist
This file lists all the files installed by the port. It is
also called the “packing list” because the package is
generated by packing the files listed here. The pathnames are
relative to the installation prefix (usually
/usr/local or
/usr/X11R6 ). If you are using the
MANn variables (as
you should be), do not list any manpages here.
Here is a small example:
bin/oneko
lib/X11/app-defaults/Oneko
lib/X11/oneko/cat1.xpm
lib/X11/oneko/cat2.xpm
lib/X11/oneko/mouse.xpm
@dirrm lib/X11/oneko
Refer to the &man.pkg.create.1; man page for details on the
packing list.
You should list all the files, but not the name directories,
in the list. Also, if the port creates directories for itself
during installation, make sure to add @dirrm
lines as necessary to remove them when the port is
deleted.
It is recommended that you keep all the filenames in this
file sorted alphabetically. It will make verifying the changes
when you upgrade the port much easier.
Creating a packing list manually can be a very tedious
task. If the port installs a large numbers of files, creating the packing list
automatically might save time.
Creating the checksum file
Just type make makesum . The ports make rules
will automatically generate the file
distinfo .
Testing the port
You should make sure that the port rules do exactly what you
want them to do, including packaging up the port. These are the
important points you need to verify.
pkg-plist does not contain anything not
installed by your port
pkg-plist contains everything that is
installed by your port
Your port can be installed multiple times using the
reinstall target
Your port cleans up
after itself upon deinstall
Recommended test ordering
make install
make package
make deinstall
pkg_add package-name
make deinstall
make reinstall
make package
Make sure that there are not any warnings issued in any of the
package and
deinstall stages. After step 3, check to
see if all the new directories are correctly deleted. Also, try
using the software after step 4, to ensure that it works correctly
when installed from a package.
Checking your port with portlint
Please use portlint to see if your port
conforms to our guidelines. The portlint program
is part of the ports collection. In particular, you may want to
check if the Makefile is in
the right shape and the package is named
appropriately.
Submitting the port
First, make sure you have read the DOs and DON'Ts section.
Now that you are happy with your port, the only thing remaining
is to put it in the main FreeBSD ports tree and make everybody else
happy about it too. We do not need your work
directory or the pkgname.tgz package, so delete
them now. Next, simply include the output of shar `find
port_dir` in a bug report and send it with the
&man.send-pr.1; program (see Bug
Reports and General Commentary for more information about
&man.send-pr.1;. If the uncompressed port is larger than 20KB,
you should compress it into a tarfile and use &man.uuencode.1;
before including it in the bug report (uuencoded tarfiles are
acceptable even if the bug report is smaller than 20KB but are not
preferred). Be sure to classify the bug report as category
ports and class
change-request (Do not mark the report
confidential !).
Also add a short description of the program you ported
to the Description
field of the PR and
the shar or uuencoded tarfile to the
Fix
field. The latter one helps the committers
a lot, who use scripts for the ports-work.
One more time, do not include the original source
distfile, the work directory, or the package
you built with make package .
In the past, we asked you to upload new port submissions in
our ftp site (ftp.FreeBSD.org ). This
is no longer recommended as read access is turned off on the
incoming/ directory of that site due to the
large amount of pirated software showing up there.
We will look at your port, get back to you if necessary, and put
it in the tree. Your name will also appear in the list of
“Additional FreeBSD contributors” in the FreeBSD
Handbook and other files. Isn't that great?!? :-)
You can make our work a lot easier, if you use a good
description in the synopsis of the problem report.
We prefer something like
“New port: <short description of the port>” for
new ports and
“Update port: <category>/<port> <short description
of the update>” for port updates.
If you stick to this scheme, the chance that one takes a look at
your PR soon is much bigger.
Slow Porting
Ok, so it was not that simple, and the port required some
modifications to get it to work. In this section, we will explain,
step by step, how to modify it to get it to work with the ports
paradigm.
How things work
First, this is the sequence of events which occurs when the user
first types make in your port's directory.
You may find that having bsd.port.mk in another
window while you read this really helps to understand it.
But do not worry if you do not really understand what
bsd.port.mk is doing, not many people do...
:->
The fetch target is run. The
fetch target is responsible for making
sure that the tarball exists locally in
DISTDIR . If fetch
cannot find the required files in DISTDIR it
will look up the URL MASTER_SITES , which is
set in the Makefile, as well as our main ftp site at ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/distfiles/ ,
where we put sanctioned distfiles as backup. It will then
attempt to fetch the named distribution file with
FETCH , assuming that the requesting site has
direct access to the Internet. If that succeeds, it will save
the file in DISTDIR for future use and
proceed.
The extract target is run. It
looks for your port's distribution file (typically a gzip'd
tarball) in DISTDIR and unpacks it into a
temporary subdirectory specified by WRKDIR
(defaults to work ).
The patch target is run. First,
any patches defined in PATCHFILES are
applied. Second, if any patch files named
patch-* are found in
PATCHDIR (defaults to the
files subdirectory), they are applied at
this time in alphabetical order.
The configure target is run. This
can do any one of many different things.
If it exists, scripts/configure is
run.
If HAS_CONFIGURE or
GNU_CONFIGURE is set,
WRKSRC /configure is
run.
If USE_IMAKE is set,
XMKMF (default: xmkmf
-a ) is run.
The build target is run. This is
responsible for descending into the port's private working
directory (WRKSRC ) and building it. If
USE_GMAKE is set, GNU make
will be used, otherwise the system make will
be used.
The above are the default actions. In addition, you can define
targets
pre-something or
post-something ,
or put scripts with those names, in the scripts
subdirectory, and they will be run before or after the default
actions are done.
For example, if you have a post-extract
target defined in your Makefile, and a file
pre-build in the scripts
subdirectory, the post-extract target will
be called after the regular extraction actions, and the
pre-build script will be executed before the
default build rules are done. It is recommended that you use
Makefile targets if the actions are simple
enough, because it will be easier for someone to figure out what
kind of non-default action the port requires.
The default actions are done by the
bsd.port.mk targets
do-something .
For example, the commands to extract a port are in the target
do-extract . If you are not happy with the
default target, you can fix it by redefining the
do-something
target in your Makefile .
The “main” targets (e.g.,
extract ,
configure , etc.) do nothing more than
make sure all the stages up to that one are completed and call
the real targets or scripts, and they are not intended to be
changed. If you want to fix the extraction, fix
do-extract , but never ever touch
extract !
Now that you understand what goes on when the user types
make , let us go through the recommended steps to
create the perfect port.
Getting the original sources
Get the original sources (normally) as a compressed tarball
(foo .tar.gz or
foo .tar.Z ) and copy
it into DISTDIR . Always use
mainstream sources when and where you
can.
If you cannot find a ftp/http site that is well-connected to the
net, or can only find sites that have irritatingly non-standard
formats, you might want to put a copy on a reliable ftp or http
server that you control (e.g., your home page). Make sure you set
MASTER_SITES to reflect your choice.
If you cannot find somewhere convenient and reliable to put the
distfile
we can “house” it ourselves
on ftp.FreeBSD.org .
The distfile must be placed into
~/public_distfiles/ of someone's
freefall account.
Ask the person who commits your port to do this.
This person will also set MASTER_SITES to
MASTER_SITE_LOCAL and
MASTER_SITE_SUBDIR to their
freefall username.
If your port's distfile changes all the time for no good reason,
consider putting the distfile in your home page and listing it as
the first MASTER_SITES . This will prevent users
from getting checksum mismatch errors, and
also reduce the workload of maintainers of our ftp site. Also, if
there is only one master site for the port, it is recommended that
you house a backup at your site and list it as the second
MASTER_SITES .
If your port requires some additional `patches' that are
available on the Internet, fetch them too and put them in
DISTDIR . Do not worry if they come from a site
other than where you got the main source tarball, we have a way to
handle these situations (see the description of PATCHFILES below).
Modifying the port
Unpack a copy of the tarball in a private directory and make
whatever changes are necessary to get the port to compile properly
under the current version of FreeBSD. Keep careful
track of everything you do, as you will be automating
the process shortly. Everything, including the deletion, addition,
or modification of files should be doable using an automated script
or patch file when your port is finished.
If your port requires significant user interaction/customization
to compile or install, you should take a look at one of Larry Wall's
classic Configure scripts and perhaps do
something similar yourself. The goal of the new ports collection is
to make each port as “plug-and-play” as possible for the
end-user while using a minimum of disk space.
Unless explicitly stated, patch files, scripts, and other
files you have created and contributed to the FreeBSD ports
collection are assumed to be covered by the standard BSD copyright
conditions.
Patching
In the preparation of the port, files that have been added or
changed can be picked up with a recursive diff for later feeding to
patch. Each set of patches you wish to apply should be collected
into a file named
patch-* where
* denotes the sequence in which the
patches will be applied — these are done in
alphabetical order , thus aa
first, ab second and so on. If you wish,
you can use names that indicate the pathnames of the files that
are patched, such as patch-Imakefile or
patch-src-config.h . These files should
be stored in PATCHDIR , from where they will be
automatically applied. All patches should be relative to
WRKSRC (generally the directory your port's
tarball unpacks itself into, that being where the build is done).
To make fixes and upgrades easier, you should avoid having more than
one patch fix the same file (e.g., patch-aa and
patch-ab both changing
WRKSRC /foobar.c ).
Configuring
Include any additional customization commands in your
configure script and save it in the
scripts subdirectory. As mentioned above, you
can also do this with Makefile targets and/or
scripts with the name pre-configure or
post-configure .
Handling user input
If your port requires user input to build, configure, or install,
then set IS_INTERACTIVE in your Makefile. This
will allow “overnight builds” to skip your port if the
user sets the variable BATCH in his environment (and
if the user sets the variable INTERACTIVE , then
only those ports requiring interaction are
built).
It is also recommended that if there are reasonable default
answers to the questions, you check the
PACKAGE_BUILDING variable and turn off the
interactive script when it is set. This will allow us to build the
packages for CD-ROMs and ftp.
Configuring the Makefile
Configuring the Makefile is pretty simple, and again we suggest
that you look at existing examples before starting. Also, there is a
sample Makefile in this
handbook, so take a look and please follow the ordering of variables
and sections in that template to make your port easier for others to
read.
Now, consider the following problems in sequence as you design
your new Makefile:
The original source
Does it live in DISTDIR as a standard
gzip'd tarball named something like
foozolix-1.2.tar.gz ? If so, you can go on
to the next step. If not, you should look at overriding any of
the DISTNAME , EXTRACT_CMD ,
EXTRACT_BEFORE_ARGS ,
EXTRACT_AFTER_ARGS ,
EXTRACT_SUFX , or DISTFILES
variables, depending on how alien a format your port's
distribution file is. (The most common case is
EXTRACT_SUFX=.tar.Z , when the tarball is
condensed by regular compress , not
gzip .)
In the worst case, you can simply create your own
do-extract target to override the
default, though this should be rarely, if ever,
necessary.
PORTNAME and PORTVERSION
You should set PORTNAME to the
base name of your port, and PORTVERSION
to the version number of the port.
PORTREVISION and
PORTEPOCH
PORTREVISION
The PORTREVISION variable is a
monotonically increasing value which is reset to 0 with
every increase of PORTVERSION (i.e.
every time a new official vendor release is made), and
appended to the package name if non-zero.
PORTREVISION is increased each time a
change is made to the FreeBSD port which significantly
affects the content or stucture of the derived
package.
Examples of when PORTREVISION should be bumped:
Addition of patches to correct security
vulnerabilities, bugs, or to add new functionality to
the FreeBSD port.
Changes to the port makefile to enable or disable
compile-time options in the package.
Changes in the packing list or the install-time
behaviour of the package (e.g. change to a script
which generates initial data for the package, like ssh
host keys).
Version bump of a port's shared library dependency
(in this case, someone trying to install the old
package after installing a newer version of the
dependency will fail since it will look for the old
libfoo.x instead of libfoo.(x+1)).
Silent changes to the port distfile which have
significant functional differences, i.e. changes to
the distfile requiring a correction to
distinfo with no corresponding change to
PORTVERSION , where a diff
-ru of the old and new versions shows
non-trivial changes to the code.
Examples of changes which do not require a
PORTREVISION bump:
Style changes to the port skeleton with no
functional change to what appears in the resulting
package.
Changes to MASTER_SITES or
other functional changes to the port which do not
effect the resulting package.
Trivial patches to the distfile such as correction
of typos, which are not important enough that users of
the package should go to the trouble of
upgrading.
Build fixes which cause a package to become
compilable where it was previously failing (as long as
the changes do not introduce any functional change on
any other platforms on which the port did previously
build). Since PORTREVISION reflects
the content of the package, if no package was
previously buildable then there is no need to increase
PORTREVISION to mark a
change.
A rule of thumb is to ask yourself whether a change
committed to a port is something which someone, somewhere,
would benefit from having (either because of an
enhancement, fix, or by virtue that the new package will
actually work for them). If yes, the
PORTREVISION should be bumped so that
automated tools (e.g. pkg_version )
will highlight the fact that a new package is
available.
PORTEPOCH
From time to time a software vendor or FreeBSD porter
will do something silly and release a version of their
software which is actually numerically less than the
previous version. An example of this is a port which goes
from foo-20000801 to foo-1.0 (the former will be
incorrectly treated as a newer version since 20000801 is a
numerically greater value than 1).
In situations such as this, the
PORTEPOCH version should be increased.
If PORTEPOCH is nonzero it is appended
to the package name as described in section 0 above.
PORTEPOCH is never decreased or reset
to zero, because that would cause comparison to a package
from an earlier epoch to fail (i.e. the package would not
be detected as out of date): the new version number (e.g.
1.0,1 in the above example) is still
numerically less than the previous version (2000801), but
the ,1 suffix is treated specially by
automated tools and found to be greater than the implied
suffix ",0" on the earlier package)
It is expected that PORTEPOCH will
not be used for the majority of ports, and that sensible
use of PORTVERSION can often pre-empt
it becoming necessary if a future release of the software
should change the version structure. However, care is
needed by FreeBSD porters when a vendor release is made
without an official version number - such as a code
"snapshot" release. The temptation is to label the
release with the release date, which will cause problems
as in the example above when a new "official" release is
made.
For example, if a snapshot release is made on the date
20000917, and the previous version of the software was
version 1.2, the snapshot release should be given a
PORTVERSION of 1.2.20000917 or similar,
not 20000917, so that the succeeding release, say 1.3, is
still a numerically greater value.
Example of PORTREVISION and
PORTEPOCH usage
The gtkmumble port, version 0.10, is committed to the
ports collection.
PORTNAME= gtkmumble
PORTVERSION= 0.10
PKGNAME becomes
gtkmumble-0.10 .
A security hole is discovered which requires a local
FreeBSD patch. PORTREVISION is bumped
accordingly.
PORTNAME= gtkmumble
PORTVERSIOn= 0.10
PORTREVISION= 1
PKGNAME becomes
gtkmumble-0.10_1
A new version is released by the vendor, numbered 0.2
(it turns out the author actually intended
0.10 to actually mean
0.1.0 , not what comes after
0.9
- oops, too late now). Since the new minor
version 2 is numerically less than the
previous version 10 the
PORTEPOCH must be bumped to manually
force the new package to be detected as "newer". Since it
is a new vendor release of the code,
PORTREVISION is reset to 0 (or removed
from the makefile).
PORTNAME= gtkmumble
PORTVERSION= 0.2
PORTEPOCH= 1
PKGNAME becomes
gtkmumble-0.2,1
The next release is 0.3. Since
PORTEPOCH never decreases, the version
variables are now:
PORTNAME= gtkmumble
PORTVERSION= 0.3
PORTEPOCH= 1
PKGNAME becomes
gtkmumble-0.3,1
If PORTEPOCH were reset
to 0 with this upgrade, someone who had
installed the gtkmumble-0.10_1 package would not detect
the gtkmumble-0.3 package as newer, since
3 is still numerically less than
10 .
PKGNAMEPREFIX and PKGNAMESUFFIX
Two optional variables, PKGNAMEPREFIX and
PKGNAMESUFFIX , are combined with
PORTNAME and
PORTVERSION to
form PKGNAME as
${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION} .
Make sure this conforms to our guidelines for a good package
name. In particular, you are not allowed to use a
hyphen (- ) in
PORTVERSION . Also, if the package name
has the language- or the
compiled.specifics part, use
PKGNAMEPREFIX and
PKGNAMESUFFIX , respectively. Do not make
them part of PORTNAME .
DISTNAME
DISTNAME is the name of the port as
called by the authors of the software.
DISTNAME defaults to
${PORTNAME}-${PORTVERSION} , so override it if necessary.
DISTNAME is only used in two places.
First, the distribution file list
(DISTFILES ) defaults to
${DISTNAME} ${EXTRACT_SUFX} .
Second, the distribution file is expected to extract into a
subdirectory named WRKSRC , which defaults
to work/${DISTNAME} .
PKGNAMEPREFIX and
PKGNAMESUFFIX do not affect
DISTNAME . Also note that when
WRKSRC is equal to
work/${PORTNAME}-${PORTVERSION}
while the original source archive is named something other than
${PORTNAME}-${PORTVERSION}${EXTRACT_SUFX} ,
you should probably leave DISTNAME
alone— you are better off defining
DISTFILES than having to set both
DISTNAME and WRKSRC
(and possibly EXTRACT_SUFX ).
CATEGORIES
When a package is created, it is put under
/usr/ports/packages/All and links are made from
one or more subdirectories of
/usr/ports/packages . The names of these
subdirectories are specified by the variable
CATEGORIES . It is intended to make life easier
for the user when he is wading through the pile of packages on the
ftp site or the CD-ROM. Please take a look at the existing categories and pick the ones
that are suitable for your port.
This list also determines where in the ports tree the port is
imported. If you put more than one category here, it is assumed
that the port files will be put in the subdirectory with the name in
the first category. See the categories section for more
discussion about how to pick the right categories.
If your port truly belongs to something that is different from
all the existing ones, you can even create a new category name. In
that case, please send mail to the &a.ports; to propose a new
category.
MASTER_SITES
Record the directory part of the ftp/http-URL pointing at the
original tarball in MASTER_SITES . Do not forget
the trailing slash (/ )!
The make macros will try to use this
specification for grabbing the distribution file with
FETCH if they cannot find it already on the
system.
It is recommended that you put multiple sites on this list,
preferably from different continents. This will safeguard against
wide-area network problems, and we are even planning to add support
for automatically determining the closest master site and fetching
from there!
If the original tarball is part of one of the popular
archives such as X-contrib, GNU, or Perl CPAN, you may be able
refer to those sites in an easy compact form using
MASTER_SITE_*
(e.g., MASTER_SITE_XCONTRIB and
MASTER_SITE_PERL_GNU ). Simply set
MASTER_SITES to one of these variables and
MASTER_SITE_SUBDIR to the path within the
archive. Here is an example:
MASTER_SITES= ${MASTER_SITE_XCONTRIB}
MASTER_SITE_SUBDIR= applications
These variables are defined in
/usr/ports/Mk/bsd.sites.mk . There are
new archives added all the time, so make sure to check the
latest version of this file before submitting a port.
The user can also set the MASTER_SITE_*
variables in /etc/make.conf to override our
choices, and use their favorite mirrors of these popular archives
instead.
PATCHFILES
If your port requires some additional patches that are available
by ftp or http, set PATCHFILES to the names of
the files and PATCH_SITES to the URL of the
directory that contains them (the format is the same as
MASTER_SITES ).
If the patch is not relative to the top of the source tree
(i.e., WRKSRC ) because it contains some extra
pathnames, set PATCH_DIST_STRIP accordingly. For
instance, if all the pathnames in the patch have an extra
foozolix-1.0/ in front of the filenames, then set
PATCH_DIST_STRIP=-p1 .
Do not worry if the patches are compressed; they will be
decompressed automatically if the filenames end with
.gz or .Z .
If the patch is distributed with some other files, such as
documentation, in a gzip'd tarball, you cannot just use
PATCHFILES . If that is the case, add the name
and the location of the patch tarball to
DISTFILES and MASTER_SITES .
Then, use the EXTRA_PATCHES variable to
point to those files and bsd.port.mk
will automatically apply them for you. In particular, do
not copy patch files into the
PATCHDIR directory—that directory may
not be writable.
Note that the tarball will have been extracted alongside the
regular source by then, so there is no need to explicitly extract
it if it is a regular gzip'd or compress'd tarball. If you do the
latter, take extra care not to overwrite something that already
exists in that directory. Also, do not forget to add a command to
remove the copied patch in the pre-clean
target.
MAINTAINER
Set your mail-address here. Please. :-)
For a detailed description of the responsibilities of maintainers,
refer to the MAINTAINER on
Makefiles section.
Dependencies
Many ports depend on other ports. There are five variables that
you can use to ensure that all the required bits will be on the
user's machine. There are also some pre-supported dependency
variables for common cases, plus a few more to control the behaviour
of dependencies.
LIB_DEPENDS
This variable specifies the shared libraries this port depends
on. It is a list of
lib :dir :target
tuples where lib is the name of the
shared library, dir is the
directory in which to find it in case it is not available, and
target is the target to call in that
directory. For example, LIB_DEPENDS=
jpeg.9:${PORTSDIR}/graphics/jpeg:install
will check for a shared jpeg library with major version 9, and
descend into the graphics/jpeg subdirectory
of your ports tree to build and install it if it is not found.
The target part can be omitted if it is
equal to DEPENDS_TARGET (which defaults to
install ).
The lib part is an argument given
to ldconfig -r | grep -wF . There shall be no
regular expressions in this variable.
The dependency is checked twice, once from within the
extract target and then from within the
install target. Also, the name of the
dependency is put into the package so that
pkg_add will automatically install it if it is
not on the user's system.
RUN_DEPENDS
This variable specifies executables or files this port depends
on during run-time. It is a list of
path :dir :target
tuples where path is the name of the
executable or file, dir is the
directory in which to find it in case it is not available, and
target is the target to call in that
directory. If path starts with a slash
(/ ), it is treated as a file and its existence
is tested with test -e ; otherwise, it is
assumed to be an executable, and which -s is
used to determine if the program exists in the user's search
path.
For example,
RUN_DEPENDS= ${PREFIX}/etc/innd:${PORTSDIR}/news/inn \
wish8.0:${PORTSDIR}/x11-toolkits/tk80
will check if the file or directory
/usr/local/etc/innd exists, and build and
install it from the news/inn subdirectory of
the ports tree if it is not found. It will also see if an
executable called wish8.0 is in your search
path, and descend into the x11-toolkits/tk80
subdirectory of your ports tree to build and install it if it is
not found.
In this case, innd is actually an
executable; if an executable is in a place that is not expected
to be in a normal user's search path, you should use the full
pathname.
The dependency is checked from within the
install target. Also, the name of the
dependency is put in to the package so that
pkg_add will automatically install it if it is
not on the user's system. The target
part can be omitted if it is the same as
DEPENDS_TARGET .
BUILD_DEPENDS
This variable specifies executables or files this port
requires to build. Like RUN_DEPENDS , it is a
list of
path :dir :target
tuples. For example, BUILD_DEPENDS=
unzip:${PORTSDIR}/archivers/unzip will check
for an executable called unzip , and descend
into the archivers/unzip subdirectory of your
ports tree to build and install it if it is not found.
“build” here means everything from extraction to
compilation. The dependency is checked from within the
extract target. The
target part can be omitted if it is
the same as DEPENDS_TARGET
FETCH_DEPENDS
This variable specifies executables or files this port
requires to fetch. Like the previous two, it is a list of
path :dir :target
tuples. For example, FETCH_DEPENDS=
ncftp2:${PORTSDIR}/net/ncftp2 will check for an
executable called ncftp2 , and descend into the
net/ncftp2 subdirectory of your ports tree to
build and install it if it is not found.
The dependency is checked from within the
fetch target. The
target part can be omitted if it is the
same as DEPENDS_TARGET .
DEPENDS
If there is a dependency that does not fall into either of the
above four categories, or your port requires having the source of
the other port extracted in addition to having it installed,
then use this variable. This is a list of
dir :target ,
as there is nothing to check, unlike the previous four. The
target part can be omitted if it is the
same as DEPENDS_TARGET .
Common dependency variables
Define USE_XLIB=yes if your port requires
the X Window System to be installed (it is implied by
USE_IMAKE ). Define
USE_GMAKE=yes if your port requires GNU
make instead of BSD make .
Define USE_AUTOCONF=yes if your port requires
GNU autoconf to be run. Define USE_QT=yes if
your port uses the latest qt toolkit. Use
USE_PERL5=yes if your port requires version 5
of the perl language. (The last is especially important since
some versions of FreeBSD have perl5 as part of the base system
while others do not.)
Notes on dependencies
As mentioned above, the default target to call when a
dependency is required is DEPENDS_TARGET .
It defaults to install . This is a user
variable; it is never defined in a port's
Makefile . If your port needs a special way
to handle a dependency, use the :target part of
the *_DEPENDS variables instead of redefining
DEPENDS_TARGET .
When you type make clean , its dependencies
are automatically cleaned too. If you do not wish this to happen,
define the variable NOCLEANDEPENDS in your
environment.
To depend on another port unconditionally, use the
variable ${NONEXISTENT} as the first field
of BUILD_DEPENDS or
RUN_DEPENDS . Use this only when you need to
the to get to the source of the other port. You can often save
compilation time by specifying the target too. For
instance
BUILD_DEPENDS= ${NONEXISTENT}:${PORTSDIR}/graphics/jpeg:extract
will always descend to the JPEG port and extract it.
Do not use DEPENDS unless there is no other
way the behaviour you want can be accomplished. It will cause the
other port to always be built (and installed, by default), and the
dependency will go into the packages as well. If this is really
what you need, you should probably write it as
BUILD_DEPENDS and
RUN_DEPENDS instead—at least the
intention will be clear.
Optional dependencies
Some large applications can be built in a number of
configurations, adding functionality if one of a number of
libraries or applications is available. Since not all users
want those libraries or applications, the ports system
provides hooks that the port author can use to decide which
configuration should be built. Supporting these properly will
make uses happy, and effectively provide 2 or more ports for the
price of one.
The easiest of these to use is
WITHOUT_X11 . If the port can be built both
with and without X support, then it should normally be built
with X support. If WITHOUT_X11 is defined,
then the version that does not have X support should be
built.
Various parts of GNOME have such knobs, though they are
slightly more difficult to use. The variables to use in the
Makefile are WANT_*
and HAVE_* . If the application can be
built both with or without one of the dependencies listed
below, then the Makefile should set
WANT_PKG , and should build the version that
uses PKG if HAVE_PKG
is defined.
The WANT_* variables currently
supported this way are WANT_GLIB ,
WANT_GTK , WANT_ESOUND ,
WANT_IMLIB , and
WANT_GNOME .
Building mechanisms
If your package uses GNU make , set
USE_GMAKE=yes . If your package uses
configure , set
HAS_CONFIGURE=yes . If your package uses GNU
configure , set
GNU_CONFIGURE=yes (this implies
HAS_CONFIGURE ). If you want to give some extra
arguments to configure (the default argument list
--prefix=${PREFIX} for GNU
configure and empty for non-GNU
configure ), set those extra arguments in
CONFIGURE_ARGS . If your package uses GNU
autoconf , set
USE_AUTOCONF=yes . This implies
GNU_CONFIGURE , and will cause
autoconf to be run before
configure .
If your package is an X application that creates
Makefile s from Imakefile s
using imake , then set
USE_IMAKE=yes . This will cause the configure
stage to automatically do an xmkmf -a . If the
-a flag is a problem for your port, set
XMKMF=xmkmf . If the port uses
imake but does not understand the
install.man target,
NO_INSTALL_MANPAGES=yes should be set. In
addition, the author of the original port should be shot. :->
If your port's source Makefile has
something else than all as the main build
target, set ALL_TARGET accordingly. Same goes
for install and
INSTALL_TARGET .
Special considerations
There are some more things you have to take into account when you
create a port. This section explains the most common of those.
Shared Libraries
If your port installs one or more shared libraries, define a
INSTALLS_SHLIB make variable, which will instruct
a bsd.port.mk to run
${LDCONFIG} -m on the directory where the
new library is installed (usually
PREFIX /lib ) during
post-install target to register it into the
shared library cache. This variable, when defined, will also
facilitate addition of an appropriate
@exec /sbin/ldconfig -m and
@unexec /sbin/ldconfig -R pair into your
pkg-plist file, so that a user who installed
the package can start using the shared library immediately and
deinstallation will not cause the system to still believe the
library is there.
If you need, you can override default location where the new
library is installed by defining LDCONFIG_DIRS
make variable, which should contain a list of directories into which
shared libraries are to be installed. For example if your port
installs shared libraries into
PREFIX /lib/foo and
PREFIX /lib/bar directories
you could use the following in your
Makefile :
INSTALLS_SHLIB= yes
LDCONFIG_DIRS= %%PREFIX%%/lib/foo %%PREFIX%%/lib/bar
Note that content of LDCONFIG_DIRS is passed
through &man.sed.1; just like the rest of pkg-plist ,
so PLIST_SUB substitutions also apply here. It is
recommended that you use %%PREFIX%% for
PREFIX , %%LOCALBASE%% for
LOCALBASE and %%X11BASE%% for
X11BASE .
MASTERDIR
If your port needs to build slightly different versions of
packages by having a variable (for instance, resolution, or paper
size) take different values, create one subdirectory per package to
make it easier for users to see what to do, but try to share as many
files as possible between ports. Typically you only need a very short
Makefile in all but one of the directories if you
use variables cleverly. In the sole Makefiles ,
you can use MASTERDIR to specify the directory
where the rest of the files are. Also, use a variable as part of
PKGNAMESUFFIX so
the packages will have different names.
This will be best demonstrated by an example. This is part of
japanese/xdvi300/Makefile ;
PORTNAME= xdvi
PORTVERSION= 17
PKGNAMEPREFIX= ja-
PKGNAMESUFFIX= ${RESOLUTION}
:
# default
RESOLUTION?= 300
.if ${RESOLUTION} != 118 && ${RESOLUTION} != 240 && \
${RESOLUTION} != 300 && ${RESOLUTION} != 400
@${ECHO} "Error: invalid value for RESOLUTION: \"${RESOLUTION}\""
@${ECHO} "Possible values are: 118, 240, 300 (default) and 400."
@${FALSE}
.endif
japanese/xdvi300 also has all the regular
patches, package files, etc. If you type make
there, it will take the default value for the resolution (300) and
build the port normally.
As for other resolutions, this is the entire
xdvi118/Makefile :
RESOLUTION= 118
MASTERDIR= ${.CURDIR}/../xdvi300
.include ${MASTERDIR}/Makefile
(xdvi240/Makefile and
xdvi400/Makefile are similar). The
MASTERDIR definition tells
bsd.port.mk that the regular set of
subdirectories like FILESDIR and
SCRIPTDIR are to be found under
xdvi300 . The RESOLUTION=118
line will override the RESOLUTION=300 line in
xdvi300/Makefile and the port will be built with
resolution set to 118.
Shared library versions
Please read our policy on
shared library versioning to understand what to do with
shared library versions in general. Do not blindly assume software
authors know what they are doing; many of them do not. It is very
important that these details are carefully considered, as we have
quite a unique situation where we are trying to have dozens of
potentially incompatible software pairs co-exist. Careless port
imports have caused great trouble regarding shared libraries in the
past (ever wondered why the port jpeg-6b has a
shared library version of 9?). If in doubt, send a message to the
&a.ports;. Most of the time, your job ends by determining the right
shared library version and making appropriate patches to implement
it.
Manpages
The MAN[1-9LN] variables will automatically add
any manpages to pkg-plist (this means you must
not list manpages in the
pkg-plist —see generating PLIST for more). It also
makes the install stage automatically compress or uncompress manpages
depending on the setting of NOMANCOMPRESS in
/etc/make.conf .
If your port tries to install multiple names for manpages using
symlinks or hardlinks, you must use the MLINKS
variable to identify these. The link installed by your port will
be destroyed and recreated by bsd.port.mk
to make sure it points to the correct file. Any manpages
listed in MLINKS must not be listed in the
pkg-plist .
To specify whether the manpages are compressed upon installation,
use the MANCOMPRESSED variable. This variable can
take three values, yes , no and
maybe . yes means manpages are
already installed compressed, no means they are
not, and maybe means the software already respects
the value of NOMANCOMPRESS so
bsd.port.mk does not have to do anything
special.
MANCOMPRESSED is automatically set to
yes if USE_IMAKE is set and
NO_INSTALL_MANPAGES is not set, and to
no otherwise. You do not have to explicitly define
it unless the default is not suitable for your port.
If your port anchors its man tree somewhere other than
PREFIX , you can use the
MANPREFIX to set it. Also, if only manpages in
certain sections go in a non-standard place, such as some Perl modules
ports, you can set individual man paths using
MANsect PREFIX (where
sect is one of 1-9 ,
L or N ).
If your manpages go to language-specific subdirectories, set the
name of the languages to MANLANG . The value of
this variable defaults to "" (i.e., English
only).
Here is an example that puts it all together.
MAN1= foo.1
MAN3= bar.3
MAN4= baz.4
MLINKS= foo.1 alt-name.8
MANLANG= "" ja
MAN3PREFIX= ${PREFIX}/share/foobar
MANCOMPRESSED= yes
This states that six files are installed by this port;
${PREFIX}/man/man1/foo.1.gz
${PREFIX}/man/ja/man1/foo.1.gz
${PREFIX}/share/foobar/man/man3/bar.3.gz
${PREFIX}/share/foobar/man/ja/man3/bar.3.gz
${PREFIX}/man/man4/baz.4.gz
${PREFIX}/man/ja/man4/baz.4.gz
Additionally ${PREFIX}/man/man8/alt-name.8.gz
may or may not be installed by your port. Regardless, a
symlink will be made to join the foo(1) manpage and
alt-name(8) manpage.
Ports that require Motif
There are many programs that require a Motif library (available
from several commercial vendors, while there is a free clone reported
to be able to run many applications in
x11-toolkits/lesstif ) to compile. Since it is a
popular toolkit and their licenses usually permit redistribution of
statically linked binaries, we have made special provisions for
handling ports that require Motif in a way that we can easily compile
binaries linked either dynamically (for people who are compiling from
the port) or statically (for people who distribute packages).
REQUIRES_MOTIF
If your port requires Motif, define this variable in the
Makefile. This will prevent people who do not own a copy of Motif
from even attempting to build it.
MOTIFLIB
This variable will be set by bsd.port.mk to
be the appropriate reference to the Motif library. Please patch the
source to use this wherever the Motif library is referenced in the
Makefile or
Imakefile .
There are two common cases:
If the port refers to the Motif library as
-lXm in its Makefile or
Imakefile , simply substitute
${MOTIFLIB} for it.
If the port uses XmClientLibs in its
Imakefile , change it to
${MOTIFLIB} ${XTOOLLIB}
${XLIB} .
Note that MOTIFLIB (usually) expands to
-L/usr/X11R6/lib -lXm or
/usr/X11R6/lib/libXm.a , so there is no need to
add -L or -l in front.
X11 fonts
If your port installs fonts for the X Window system, put them in
X11BASE /lib/X11/fonts/local .
This directory is new to XFree86 release 3.3.3. If it does not exist,
please create it, and print out a message urging the user to update
their XFree86 to 3.3.3 or newer, or at least add this directory to the
font path in /etc/XF86Config .
Info files
The new version of texinfo (included in 2.2.2-RELEASE and onwards)
contains a utility called install-info to add and
delete entries to the dir file. If your port
installs any info documents, please follow these instructions so your
port/package will correctly update the user's
PREFIX /info/dir file. (Sorry
for the length of this section, but is it imperative to weave all the
info files together. If done correctly, it will produce a
beautiful listing, so please bear with me!
First, this is what you (as a porter) need to know
&prompt.user; install-info --help
install-info [OPTION]... [INFO-FILE [DIR-FILE]]
Install INFO-FILE in the Info directory file DIR-FILE.
Options:
--delete Delete existing entries in INFO-FILE;
don't insert any new entries.
:
--entry=TEXT Insert TEXT as an Info directory entry.
:
--section=SEC Put this file's entries in section SEC of the directory. :
This program will not actually install info
files; it merely inserts or deletes entries in the
dir file.
Here's a seven-step procedure to convert ports to use
install-info .
editors/emacs will be used as an
example.
Look at the texinfo sources and make a patch to insert
@dircategory and @direntry
statements to files that do not have them. This is part of my
patch:
--- ./man/vip.texi.org Fri Jun 16 15:31:11 1995
+++ ./man/vip.texi Tue May 20 01:28:33 1997
@@ -2,6 +2,10 @@
@setfilename ../info/vip
@settitle VIP
+@dircategory The Emacs editor and associated tools
+@direntry
+* VIP: (vip). A VI-emulation for Emacs.
+@end direntry
@iftex
@finalout
:
The format should be self-explanatory. Many authors leave a
dir file in the source tree that contains all
the entries you need, so look around before you try to write your
own. Also, make sure you look into related ports and make the
section names and entry indentations consistent (we recommend that
all entry text start at the 4th tab stop).
Note that you can put only one info entry per file because
of a bug in install-info --delete that
deletes only the first entry if you specify multiple entries in
the @direntry section.
You can give the dir entries to
install-info as arguments
(--section and --entry ) instead
of patching the texinfo sources. This probably is not a good
idea for ports because you need to duplicate the same information
in three places
(Makefile and
@exec /@unexec of
pkg-plist ; see below). However, if you have
Japanese (or other multibyte encoding) info files, you will have
to use the extra arguments to install-info
because makeinfo cannot handle those texinfo
sources. (See Makefile and
pkg-plist of japanese/skk
for examples on how to do this).
Go back to the port directory and do a make clean;
make and verify that the info files are regenerated
from the texinfo sources. Since the texinfo sources are newer than
the info files, they should be rebuilt when you type
make ; but many Makefile s
do not include correct dependencies for info files. In
emacs ' case, it was necessary to patch the main
Makefile.in so it would descend into the
man subdirectory to rebuild the info
pages.
--- ./Makefile.in.org Mon Aug 19 21:12:19 1996
+++ ./Makefile.in Tue Apr 15 00:15:28 1997
@@ -184,7 +184,7 @@
# Subdirectories to make recursively. `lisp' is not included
# because the compiled lisp files are part of the distribution
# and you cannot remake them without installing Emacs first.
-SUBDIR = lib-src src
+SUBDIR = lib-src src man
# The makefiles of the directories in $SUBDIR.
SUBDIR_MAKEFILES = lib-src/Makefile man/Makefile src/Makefile oldXMenu/Makefile
lwlib/Makefile
--- ./man/Makefile.in.org Thu Jun 27 15:27:19 1996
+++ ./man/Makefile.in Tue Apr 15 00:29:52 1997
@@ -66,6 +66,7 @@
${srcdir}/gnu1.texi \
${srcdir}/glossary.texi
+all: info
info: $(INFO_TARGETS)
dvi: $(DVI_TARGETS)
The second hunk was necessary because the default target in
the man subdir is called
info , while the main
Makefile wants to call
all . The installation of the
info info file was also removed because we
already have one with the same name in
/usr/share/info (that patch is not shown
here).
If there is a place in the Makefile that
is installing the dir file, delete it. Your
port may not be doing it. Also, remove any commands that are
otherwise mucking around with the dir
file.
--- ./Makefile.in.org Mon Aug 19 21:12:19 1996
+++ ./Makefile.in Mon Apr 14 23:38:07 1997
@@ -368,14 +368,8 @@
if [ `(cd ${srcdir}/info && /bin/pwd)` != `(cd ${infodir} && /bin/pwd)` ]; \
then \
(cd ${infodir}; \
- if [ -f dir ]; then \
- if [ ! -f dir.old ]; then mv -f dir dir.old; \
- else mv -f dir dir.bak; fi; \
- fi; \
cd ${srcdir}/info ; \
- (cd $${thisdir}; ${INSTALL_DATA} ${srcdir}/info/dir ${infodir}/dir);
\
- (cd $${thisdir}; chmod a+r ${infodir}/dir); \
for f in ccmode* cl* dired-x* ediff* emacs* forms* gnus* info* message* mh-e* sc* vip*; do \
(cd $${thisdir}; \
${INSTALL_DATA} ${srcdir}/info/$$f ${infodir}/$$f; \
chmod a+r ${infodir}/$$f); \
(This step is only necessary if you are modifying an existing
port.) Take a look at pkg-plist and delete
anything that is trying to patch up info/dir .
They may be in pkg-install or some other
file, so search extensively.
Index: pkg-plist
===================================================================
RCS file: /usr/cvs/ports/editors/emacs/pkg-plist,v
retrieving revision 1.15
diff -u -r1.15 pkg-plist
--- pkg-plist 1997/03/04 08:04:00 1.15
+++ pkg-plist 1997/04/15 06:32:12
@@ -15,9 +15,6 @@
man/man1/emacs.1.gz
man/man1/etags.1.gz
man/man1/ctags.1.gz
-@unexec cp %D/info/dir %D/info/dir.bak
-info/dir
-@unexec cp %D/info/dir.bak %D/info/dir
info/cl
info/cl-1
info/cl-2
Add a post-install target to the
Makefile to call
install-info with the installed
info files. (It is no longer necessary to create the
dir file yourself;
install-info automatically creates this
file if it does not exist.)
Index: Makefile
===================================================================
RCS file: /usr/cvs/ports/editors/emacs/Makefile,v
retrieving revision 1.26
diff -u -r1.26 Makefile
--- Makefile 1996/11/19 13:14:40 1.26
+++ Makefile 1997/05/20 10:25:09 1.28
@@ -20,5 +20,8 @@
post-install:
.for file in emacs-19.34 emacsclient etags ctags b2m
strip ${PREFIX}/bin/${file}
.endfor
+.for info in emacs vip viper forms gnus mh-e cl sc dired-x ediff ccmode
+ install-info ${PREFIX}/info/${info} ${PREFIX}/info/dir
+.endfor
.include <bsd.port.mk>
Edit pkg-plist and add equivalent
@exec statements and also
@unexec for
pkg_delete .
Index: pkg-plist
===================================================================
RCS file: /usr/cvs/ports/editors/emacs/pkg-plist,v
retrieving revision 1.15
diff -u -r1.15 pkg-plist
--- pkg-plist 1997/03/04 08:04:00 1.15
+++ pkg-plist 1997/05/20 10:25:12 1.17
@@ -16,7 +14,14 @@
man/man1/etags.1.gz
man/man1/ctags.1.gz
+@unexec install-info --delete %D/info/emacs %D/info/dir
:
+@unexec install-info --delete %D/info/ccmode %D/info/dir
info/cl
info/cl-1
@@ -87,6 +94,18 @@
info/viper-3
info/viper-4
+@exec install-info %D/info/emacs %D/info/dir
:
+@exec install-info %D/info/ccmode %D/info/dir
libexec/emacs/19.34/i386--freebsd/cvtmail
libexec/emacs/19.34/i386--freebsd/digest-doc
The @unexec install-info --delete
commands have to be listed before the info files themselves so
they can read the files. Also, the @exec
install-info commands have to be after the info
files and the @exec command that creates the
the dir file.
Test and admire your
work. :-) . Check the
dir file before and after each step.
The pkg-* files
There are some tricks we have not mentioned yet about the
pkg-* files
that come in handy sometimes.
pkg-message
If you need to display a message to the installer, you may place
the message in pkg-message . This capability is
often useful to display additional installation steps to be taken
after a pkg_add or to display licensing
information.
The pkg-message file does not need to be
added to pkg-plist . Also, it will not get
automatically printed if the user is using the port, not the
package, so you should probably display it from the
post-install target yourself.
pkg-install
If your port needs to execute commands when the binary package
is installed with pkg_add you can do this via the
pkg-install script. This script will
automatically be added to the package, and will be run twice by
pkg_add . The first time as
${SH} pkg-install ${PKGNAME}
PRE-INSTALL and the second time as
${SH} pkg-install ${PKGNAME} POST-INSTALL .
$2 can be tested to determine which mode
the script is being run in. The PKG_PREFIX
environmental variable will be set to the package installation
directory. See &man.pkg.add.1; for
additional information.
This script is not run automatically if you install the port
with make install . If you are depending on it
being run, you will have to explicitly call it from your port's
Makefile .
pkg-req
If your port needs to determine if it should install or not, you
can create a pkg-req “requirements”
script. It will be invoked automatically at
installation/deinstallation time to determine whether or not
installation/deinstallation should proceed.
Changing pkg-plist based on make
variables
Some ports, particularly the p5- ports, need to change their
pkg-plist depending on what options they are
configured with (or version of perl, in the case of p5- ports). To
make this easy, any instances in the pkg-plist of
%%OSREL%% , %%PERL_VER%% , and
%%PERL_VERSION%% will be substituted for
appropriately. The value of %%OSREL%% is the
numeric revision of the operating system (e.g.,
2.2.7 ). %%PERL_VERSION%% is
the full version number of perl (e.g., 5.00502 )
and %%PERL_VER%% is the perl version number minus
the patchlevel (e.g., 5.005 ).
If you need to make other substitutions, you can set the
PLIST_SUB variable with a list of
VAR =VALUE
pairs and instances of
%%VAR %% ' will be
substituted with VALUE in the
pkg-plist .
For instance, if you have a port that installs many files in a
version-specific subdirectory, you can put something like
OCTAVE_VERSION= 2.0.13
PLIST_SUB= OCTAVE_VERSION=${OCTAVE_VERSION}
in the Makefile and use
%%OCTAVE_VERSION%% wherever the version shows up
in pkg-plist . That way, when you upgrade the port,
you will not have to change dozens (or in some cases, hundreds) of
lines in the pkg-plist .
This substitution (as well as addition of any man pages) will be done between
the do-install and
post-install targets, by reading from
PLIST and writing to TMPPLIST
(default:
WRKDIR /.PLIST.mktmp ). So if
your port builds PLIST on the fly, do so in or
before do-install . Also, if your port
needs to edit the resulting file, do so in
post-install to a file named
TMPPLIST .
Changing the names of
pkg-* files
All the names of pkg-* files
are defined using variables so you can change them in your
Makefile if need be. This is especially useful
when you are sharing the same pkg-* files
among several ports or have to write to one of the above files (see
writing to places other than
WRKDIR for why it is a bad idea to write
directly in to the pkg-* subdirectory).
Here is a list of variable names and their default
values. (PKGDIR defaults to
${MASTERDIR} .)
Variable
Default value
COMMENT
${PKGDIR}/pkg-comment
DESCR
${PKGDIR}/pkg-descr
PLIST
${PKGDIR}/pkg-plist
PKGINSTALL
${PKGDIR}/pkg-install
PKGDEINSTALL
${PKGDIR}/pkg-deinstall
PKGREQ
${PKGDIR}/pkg-req
PKGMESSAGE
${PKGDIR}/pkg-message
Please change these variables rather than overriding
PKG_ARGS . If you change
PKG_ARGS , those files will not correctly be
installed in /var/db/pkg upon install from a
port.
Licensing Problems
Some software packages have restrictive licenses or can be in
violation of the law in some countries (such as violating a patent).
What we can do with
them varies a lot, depending on the exact wordings of the respective
licenses.
It is your responsibility as a porter to read the licensing
terms of the software and make sure that the FreeBSD project will
not be held accountable for violating them by redistributing the
source or compiled binaries either via ftp or CD-ROM. If in doubt,
please contact the &a.ports;.
There are two variables you can set in the Makefile to handle the
situations that arise frequently:
If the port has a “do not sell for profit” type of
license, set the variable NO_CDROM to a string
describing the reason why. We will make sure such ports will not go
into the CD-ROM come release time. The distfile and package will
still be available via ftp.
If the resulting package needs to be built uniquely for each
site, or the resulting binary package cannot be distributed due to
licensing; set the variable NO_PACKAGE to a
string describing the reason why. We will make sure such packages
will not go on the ftp site, nor into the CD-ROM come release time.
The distfile will still be included on both however.
If the port has legal restrictions on who can use it (e.g.,
patented stuff) or has a “no commercial use” license,
set the variable RESTRICTED to be the string
describing the reason why. For such ports, the distfiles/packages
will not be available even from our ftp sites.
The GNU General Public License (GPL), both version 1 and 2,
should not be a problem for ports.
If you are a committer, make sure you update the
ports/LEGAL file too.
Upgrading
When you notice that a port is out of date compared to the latest
version from the original authors, first make sure you have the latest
port. You can find them in the
ports/ports-current directory of the ftp mirror
sites. You may also use CVSup to keep your whole ports collection
up-to-date, as described in the Handbook .
The next step is to send a mail to the maintainer, if one is
listed in the port's Makefile . That person may
already be working on an upgrade, or have a reason to not upgrade the
port right now (because of, for example, stability problems of the new
version).
If the maintainer asks you to do the upgrade or there is not any
such person to begin with, please make the upgrade and send the
recursive diff (either unified or context diff is fine, but port
committers appear to prefer unified diff more) of the new and old
ports directories to us (e.g., if your modified port directory is
called superedit and the original as in our tree
is superedit.bak , then send us the result of
diff -ruN superedit.bak superedit ). Please examine
the output to make sure all the changes make sense. The best way to
send us the diff is by including it via &man.send-pr.1; (category
ports ). Please mention any added or deleted files
in the message, as they have to be explicitly specified to CVS when
doing a commit. If the diff is more than about 20KB, please compress
and uuencode it; otherwise, just include it in the PR as is.
Once again, please use &man.diff.1; and not &man.shar.1; to send
updates to existing ports!
Dos and Don'ts
Here is a list of common dos and don'ts that you encounter during
the porting process.You should check your own port against this list,
but you can also check ports in the PR database that others have
submitted. Submit any comments on ports you check as described in
Bug Reports and General
Commentary . Checking ports in the PR database will both make
it faster for us to commit them, and prove that you know what you are
doing.
Strip Binaries
Do strip binaries. If the original source already strips the
binaries, fine; otherwise you should add a
- post-install rule to to it yourself. Here is an
+ post-install rule to it yourself. Here is an
example:
post-install:
strip ${PREFIX}/bin/xdl
Use the &man.file.1; command on the installed executable to
check whether the binary is stripped or not. If it does not say
not stripped , it is stripped.
INSTALL_* macros
Do use the macros provided in bsd.port.mk
to ensure correct modes and ownership of files in your own
*-install targets.
INSTALL_PROGRAM is a command to install
binary executables.
INSTALL_SCRIPT is a command to install
executable scripts.
INSTALL_DATA is a command to install
sharable data.
INSTALL_MAN is a command to install
manpages and other documentation (it does not compress
anything).
These are basically the install command with
all the appropriate flags. See below for an example on how to use
them.
WRKDIR
Do not write anything to files outside
WRKDIR . WRKDIR is the only
place that is guaranteed to be writable during the port build (see
compiling ports from CDROM for an
example of building ports from a read-only tree). If you need to
modify one of the pkg-*
files, do so by redefining a variable, not by
writing over it.
WRKDIRPREFIX
Make sure your port honors WRKDIRPREFIX .
Most ports do not have to worry about this. In particular, if you
are referring to a WRKDIR of another port, note
that the correct location is
WRKDIRPREFIX PORTSDIR /subdir /name /work not PORTSDIR /subdir /name /work or .CURDIR /../../subdir /name /work or some such.
Also, if you are defining WRKDIR yourself,
make sure you prepend
${WRKDIRPREFIX}${.CURDIR} in the
front.
Differentiating operating systems and OS versions
You may come across code that needs modifications or conditional
compilation based upon what version of UNIX it is running under. If
you need to make such changes to the code for conditional
compilation, make sure you make the changes as general as possible
so that we can back-port code to FreeBSD 1.x systems and cross-port
to other BSD systems such as 4.4BSD from CSRG, BSD/386, 386BSD,
NetBSD, and OpenBSD.
The preferred way to tell 4.3BSD/Reno (1990) and newer versions
of the BSD code apart is by using the BSD macro
defined in <sys/param.h> . Hopefully that
file is already included; if not, add the code:
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
to the proper place in the .c file. We
believe that every system that defines these two symbols has
sys/param.h . If you find a system that
does not, we would like to know. Please send mail to the
&a.ports;.
Another way is to use the GNU Autoconf style of doing
this:
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
Do not forget to add -DHAVE_SYS_PARAM_H to the
CFLAGS in the Makefile for
this method.
Once you have sys/param.h included, you may
use:
#if (defined(BSD) && (BSD >= 199103))
to detect if the code is being compiled on a 4.3 Net2 code base
or newer (e.g. FreeBSD 1.x, 4.3/Reno, NetBSD 0.9, 386BSD, BSD/386
1.1 and below).
Use:
#if (defined(BSD) && (BSD >= 199306))
to detect if the code is being compiled on a 4.4 code base or
newer (e.g. FreeBSD 2.x, 4.4, NetBSD 1.0, BSD/386 2.0 or
above).
The value of the BSD macro is
199506 for the 4.4BSD-Lite2 code base. This is
stated for informational purposes only. It should not be used to
distinguish between versions of FreeBSD based only on 4.4-Lite vs.
versions that have merged in changes from 4.4-Lite2. The
__FreeBSD__ macro should be used instead.
Use sparingly:
__FreeBSD__ is defined in all versions of
FreeBSD. Use it if the change you are making
only affects FreeBSD. Porting gotchas like
the use of sys_errlist[] vs
strerror() are Berkeleyisms, not FreeBSD
changes.
In FreeBSD 2.x, __FreeBSD__ is defined to
be 2 . In earlier versions, it is
1 . Later versions will bump it to match
their major version number.
If you need to tell the difference between a FreeBSD 1.x
system and a FreeBSD 2.x or 3.x system, usually the right answer
is to use the BSD macros described above. If
there actually is a FreeBSD specific change (such as special
shared library options when using ld ) then it
is OK to use __FreeBSD__ and #if
__FreeBSD__ > 1 to detect a FreeBSD 2.x and later
system. If you need more granularity in detecting FreeBSD
systems since 2.0-RELEASE you can use the following:
#if __FreeBSD__ >= 2
#include <osreldate.h>
# if __FreeBSD_version >= 199504
/* 2.0.5+ release specific code here */
# endif
#endif
Release
__FreeBSD_version
2.0-RELEASE
119411
2.1-CURRENT
199501, 199503
2.0.5-RELEASE
199504
2.2-CURRENT before 2.1
199508
2.1.0-RELEASE
199511
2.2-CURRENT before 2.1.5
199512
2.1.5-RELEASE
199607
2.2-CURRENT before 2.1.6
199608
2.1.6-RELEASE
199612
2.1.7-RELEASE
199612
2.2-RELEASE
220000
2.2.1-RELEASE
220000 (no change)
2.2-STABLE after 2.2.1-RELEASE
220000 (no change)
2.2-STABLE after texinfo-3.9
221001
2.2-STABLE after top
221002
2.2.2-RELEASE
222000
2.2-STABLE after 2.2.2-RELEASE
222001
2.2.5-RELEASE
225000
2.2-STABLE after 2.2.5-RELEASE
225001
2.2-STABLE after ldconfig -R merge
225002
2.2.6-RELEASE
226000
2.2.7-RELEASE
227000
2.2-STABLE after 2.2.7-RELEASE
227001
2.2-STABLE after &man.semctl.2; change
227002
2.2.8-RELEASE
228000
2.2-STABLE after 2.2.8-RELEASE
228001
3.0-CURRENT before &man.mount.2; change
300000
3.0-CURRENT after &man.mount.2; change
300001
3.0-CURRENT after &man.semctl.2; change
300002
3.0-CURRENT after ioctl arg changes
300003
3.0-CURRENT after ELF conversion
300004
3.0-RELEASE
300005
3.0-CURRENT after 3.0-RELEASE
300006
3.0-STABLE after 3/4 branch
300007
3.1-RELEASE
310000
3.1-STABLE after 3.1-RELEASE
310001
3.1-STABLE after C++ constructor/destructor order
change
310002
3.2-RELEASE
320000
3.2-STABLE
320001
3.2-STABLE after binary-incompatible IPFW and
socket changes
320002
3.3-RELEASE
330000
3.3-STABLE
330001
3.3-STABLE after adding &man.mkstemp.3;
to libc
330002
3.4-RELEASE
340000
3.4-STABLE
340001
4.0-CURRENT after 3.4 branch
400000
4.0-CURRENT after change in dynamic linker
handling
400001
4.0-CURRENT after C++ constructor/destructor
order change
400002
4.0-CURRENT after functioning &man.dladdr.3;
400003
4.0-CURRENT after __deregister_frame_info dynamic
linker bug fix (also 4.0-CURRENT after EGCS 1.1.2
integration)
400004
4.0-CURRENT after &man.suser.9; API change
(also 4.0-CURRENT after newbus)
400005
4.0-CURRENT after cdevsw registration change
400006
4.0-CURRENT after the addition of so_cred for
socket level credentials
400007
4.0-CURRENT after the addition of a poll syscall
wrapper to libc_r
400008
4.0-CURRENT after the change of the kernel's
dev_t type to struct
specinfo pointer
400009
4.0-CURRENT after fixing a hole
in &man.jail.2;
400010
4.0-CURRENT after the sigset_t
datatype change
400011
4.0-CURRENT after the cutover to the GCC 2.95.2
compiler
400012
4.0-CURRENT after adding pluggable linux-mode
ioctl handlers
400013
4.0-CURRENT after importing OpenSSL
400014
4.0-CURRENT after the C++ ABI change in GCC 2.95.2
from -fvtable-thunks to -fno-vtable-thunks by
default
400015
4.0-CURRENT after importing OpenSSH
400016
4.0-RELEASE
400017
4.0-STABLE after 4.0-RELEASE
400018
4.0-STABLE after merging libxpg4 code into
libc.
400020
4.0-STABLE after upgrading Binutils to 2.10.0, ELF
branding changes, and tcsh in the base system.
400021
4.1-RELEASE
410000
4.1-STABLE after 4.1-RELEASE
410001
4.1-STABLE after &man.setproctitle.3; moved from
libutil to libc.
410002
4.1.1-RELEASE
411000
4.1.1-STABLE after 4.1.1-RELEASE
411001
4.2-RELEASE
420000
4.2-STABLE after combining libgcc.a and
libgcc_r.a, and associated GCC linkage changes.
420001
5.0-CURRENT
500000
5.0-CURRENT after adding addition ELF header fields,
and changing our ELF binary branding method.
500001
5.0-CURRENT after kld metadata changes.
500002
5.0-CURRENT after buf/bio changes.
500003
5.0-CURRENT after binutils upgrade.
500004
5.0-CURRENT after merging libxpg4 code into
libc and after TASKQ interface introduction.
500005
5.0-CURRENT after the addition of AGP
interfaces.
500006
5.0-CURRENT after Perl upgrade to 5.6.0
500007
5.0-CURRENT after the update of KAME code to
2000/07 sources.
500008
5.0-CURRENT after ether_ifattach() and
ether_ifdetach() changes.
500009
5.0-CURRENT after changing mtree defaults
back to original variant, adding -L to follow
symlinks.
500010
5.0-CURRENT after kqueue API changed.
500011
5.0-CURRENT after &man.setproctitle.3; moved from
libutil to libc.
500012
5.0-CURRENT after the first SMPng commit.
500013
5.0-CURRENT after <sys/select.h> moved to
<sys/selinfo.h>.
500014
5.0-CURRENT after combining libgcc.a and
libgcc_r.a, and associated GCC linkage changes.
500015
5.0-CURRENT after change allowing libc and libc_r
to be linked together, deprecating -pthread option.
500016
5.0-CURRENT after switch from struct ucred to
struct xucred to stabilize kernel-exported API for
mountd et al.
500017
5.0-CURRENT after addition of CPUTYPE make variable
for controlling CPU-specific optimizations.
500018
5.0-CURRENT after moving machine/ioctl_fd.h to
sys/fdcio.h
500019
5.0-CURRENT after locale names renaming.
500020
5.0-CURRENT after Bzip2 import.
500021
Note that 2.2-STABLE sometimes identifies itself as
“2.2.5-STABLE” after the 2.2.5-RELEASE. The pattern
used to be year followed by the month, but we decided to change it
to a more straightforward major/minor system starting from 2.2.
This is because the parallel development on several branches made
it infeasible to classify the releases simply by their real
release dates. If you are making a port now, you do not have to
worry about old -CURRENTs; they are listed here just for your
reference.
In the hundreds of ports that have been done, there have only
been one or two cases where __FreeBSD__ should
have been used. Just because an earlier port screwed up and used it
in the wrong place does not mean you should do so too.
Writing something after
bsd.port.mk
Do not write anything after the .include
<bsd.port.mk> line. It usually can be avoided by
including bsd.port.pre.mk somewhere in the
middle of your Makefile and
bsd.port.post.mk at the end.
You need to include either the
pre.mk /post.mk pair or
bsd.port.mk only; do not mix these two.
bsd.port.pre.mk only defines a few
variables, which can be used in tests in the
Makefile , bsd.port.post.mk
defines the rest.
Here are some important variables defined in
bsd.port.pre.mk (this is not the complete list,
please read bsd.port.mk for the complete
list).
Variable
Description
ARCH
The architecture as returned by uname
-m (e.g., i386 )
OPSYS
The operating system type, as returned by
uname -s (e.g.,
FreeBSD )
OSREL
The release version of the operating system (e.g.,
2.1.5 or
2.2.7 )
OSVERSION
The numeric version of the operating system, same as
__FreeBSD_version .
PORTOBJFORMAT
The object format of the system
(aout or elf )
LOCALBASE
The base of the “local” tree (e.g.,
/usr/local/ )
X11BASE
The base of the “X11” tree (e.g.,
/usr/X11R6 )
PREFIX
Where the port installs itself (see more on
PREFIX ).
If you have to define the variables
USE_IMAKE , USE_X_PREFIX , or
MASTERDIR , do so before including
bsd.port.pre.mk .
Here are some examples of things you can write after
bsd.port.pre.mk :
# no need to compile lang/perl5 if perl5 is already in system
.if ${OSVERSION} > 300003
BROKEN= perl is in system
.endif
# only one shlib version number for ELF
.if ${PORTOBJFORMAT} == "elf"
TCL_LIB_FILE= ${TCL_LIB}.${SHLIB_MAJOR}
.else
TCL_LIB_FILE= ${TCL_LIB}.${SHLIB_MAJOR}.${SHLIB_MINOR}
.endif
# software already makes link for ELF, but not for a.out
post-install:
.if ${PORTOBJFORMAT} == "aout"
${LN} -sf liblinpack.so.1.0 ${PREFIX}/lib/liblinpack.so
.endif
Install additional documentation
If your software has some documentation other than the standard
man and info pages that you think is useful for the user, install it
under PREFIX /share/doc .
This can be done, like the previous item, in the
post-install target.
Create a new directory for your port. The directory name should
reflect what the port is. This usually means
PORTNAME . However, if you
think the user might want different versions of the port to be
installed at the same time, you can use the whole
PKGNAME .
Make the installation dependent to the variable
NOPORTDOCS so that users can disable it in
/etc/make.conf , like this:
post-install:
.if !defined(NOPORTDOCS)
${MKDIR} ${PREFIX}/share/doc/xv
${INSTALL_MAN} ${WRKSRC}/docs/xvdocs.ps ${PREFIX}/share/doc/xv
.endif
Do not forget to add them to pkg-plist too.
(Do not worry about NOPORTDOCS here; there is
currently no way for the packages to read variables from
/etc/make.conf .)
You can also use the pkg-message file to
display messages upon installation. See the using
pkg-message section for
details.
pkg-message does not need to be added to
pkg-plist .
DIST_SUBDIR
Do not let your port clutter
/usr/ports/distfiles . If your port requires a
lot of files to be fetched, or contains a file that has a name that
might conflict with other ports (e.g.,
Makefile ), set DIST_SUBDIR
to the name of the port (${PORTNAME} or
${PKGNAMEPREFIX}${PORTNAME}
should work fine). This will change
DISTDIR from the default
/usr/ports/distfiles to
/usr/ports/distfiles/DIST_SUBDIR ,
and in effect puts everything that is required for your port into
that subdirectory.
It will also look at the subdirectory with the same name on the
backup master site at ftp.FreeBSD.org .
(Setting DISTDIR explicitly in your
Makefile will not accomplish this, so please use
DIST_SUBDIR .)
This does not affect the MASTER_SITES you
define in your Makefile.
Package information
Do include package information, i.e.
pkg-comment , pkg-descr , and
pkg-plist .
Note that these files are not used only for packaging anymore,
and are mandatory now, even if
NO_PACKAGE is set.
RCS strings
Do not put RCS strings in patches. CVS will mangle them when we
put the files into the ports tree, and when we check them out again,
they will come out different and the patch will fail. RCS strings
are surrounded by dollar ($ ) signs, and
typically start with $Id or
$RCS .
Recursive diff
Using the recurse (-r ) option to
diff to generate patches is fine, but please take
a look at the resulting patches to make sure you do not have any
unnecessary junk in there. In particular, diffs between two backup
files, Makefiles when the port uses
Imake or GNU configure , etc.,
are unnecessary and should be deleted. If you had to edit
configure.in and run
autoconf to regenerate
configure , do not take the diffs of
configure (it often grows to a few thousand
lines!); define USE_AUTOCONF=yes and take the
diffs of configure.in .
Also, if you had to delete a file, then you can do it in the
post-extract target rather than as part of
the patch. Once you are happy with the resulting diff, please split
it up into one source file per patch file.
PREFIX
Do try to make your port install relative to
PREFIX . (The value of this variable will be set
to LOCALBASE (default
/usr/local ), unless
USE_X_PREFIX or USE_IMAKE is
set, in which case it will be X11BASE (default
/usr/X11R6 ).)
Not hard-coding /usr/local or
/usr/X11R6 anywhere in the source will make the
port much more flexible and able to cater to the needs of other
sites. For X ports that use imake , this is
automatic; otherwise, this can often be done by simply replacing the
occurrences of /usr/local (or
/usr/X11R6 for X ports that do not use imake)
in the various scripts/Makefiles in the port to read
PREFIX , as this variable is automatically passed
down to every stage of the build and install processes.
Make sure your application isn't installing things in
/usr/local instead of PREFIX .
A quick test for this is to do this is:
&prompt.root; make clean; make package PREFIX=/var/tmp/port-name
If anything is installed outside of PREFIX ,
making the package creation process will complain that it
can't find the files.
+
This does not test for the existence of internal references,
or correct use of LOCALBASE for references to
files from other ports. Testing the installation in
/var/tmp/port-name
to do that that while you have it installed would do that.
Do not set USE_X_PREFIX unless your port
truly requires it (i.e., it links against X libs or it needs to
reference files in X11BASE ).
The variable PREFIX can be reassigned in your
Makefile or in the user's environment.
However, it is strongly discouraged for individual ports to set this
variable explicitly in the Makefiles .
Also, refer to programs/files from other ports with the
variables mentioned above, not explicit pathnames. For instance, if
your port requires a macro PAGER to be the full
pathname of less , use the compiler flag:
-DPAGER=\"${PREFIX}/bin/less\"
or
-DPAGER=\"${LOCALBASE}/bin/less\"
if this is an X port, instead of
-DPAGER=\"/usr/local/bin/less\". This way it will
have a better chance of working if the system administrator has
moved the whole `/usr/local' tree somewhere else.
Subdirectories
Try to let the port put things in the right subdirectories of
PREFIX . Some ports lump everything and put it in
the subdirectory with the port's name, which is incorrect. Also,
many ports put everything except binaries, header files and manual
pages in the a subdirectory of lib , which does
not bode well with the BSD paradigm. Many of the files should be
moved to one of the following: etc
(setup/configuration files), libexec
(executables started internally), sbin
(executables for superusers/managers), info
(documentation for info browser) or share
(architecture independent files). See man &man.hier.7; for details,
the rules governing
/usr pretty much apply to
/usr/local too. The exception are ports
dealing with USENET “news”. They may use
PREFIX /news as a destination
for their files.
Cleaning up empty directories
Do make your ports clean up after themselves when they are
deinstalled. This is usually accomplished by adding
@dirrm lines for all directories that are
specifically created by the port. You need to delete subdirectories
before you can delete parent directories.
:
lib/X11/oneko/pixmaps/cat.xpm
lib/X11/oneko/sounds/cat.au
:
@dirrm lib/X11/oneko/pixmaps
@dirrm lib/X11/oneko/sounds
@dirrm lib/X11/oneko
However, sometimes @dirrm will give you
errors because other ports also share the same subdirectory. You
can call rmdir from @unexec to
remove only empty directories without warning.
@unexec rmdir %D/share/doc/gimp 2>/dev/null || true
This will neither print any error messages nor cause
pkg_delete to exit abnormally even if
PREFIX /share/doc/gimp is not
empty due to other ports installing some files in there.
UIDs
If your port requires a certain user to be on the installed
system, let the pkg-install script call
pw to create it automatically. Look at
net/cvsup-mirror for an example.
If your port must use the same user/group ID number when it is
installed as a binary package as when it was compiled, then you must
choose a free UID from 50 to 99 and register it below. Look at
japanese/Wnn for an example.
Make sure you do not use a UID already used by the system or
other ports. This is the current list of UIDs between 50 and
99.
majordom:*:54:54:Majordomo Pseudo User:/usr/local/majordomo:/nonexistent
cyrus:*:60:60:the cyrus mail server:/nonexistent:/nonexistent
gnats:*:61:1:GNATS database owner:/usr/local/share/gnats/gnats-db:/bin/sh
uucp:*:66:66:UUCP pseudo-user:/var/spool/uucppublic:/usr/libexec/uucp/uucico
xten:*:67:67:X-10 daemon:/usr/local/xten:/nonexistent
pop:*:68:6:Post Office Owner (popper):/nonexistent:/nonexistent
wnn:*:69:7:Wnn:/nonexistent:/nonexistent
ifmail:*:70:66:Ifmail user:/nonexistent:/nonexistent
pgsql:*:70:70:PostgreSQL pseudo-user:/usr/local/pgsql:/bin/sh
ircd:*:72:72:IRCd hybrid:/nonexistent:/nonexistent
alias:*:81:81:QMail user:/var/qmail/alias:/nonexistent
qmaill:*:83:81:QMail user:/var/qmail:/nonexistent
qmaild:*:82:81:QMail user:/var/qmail:/nonexistent
qmailq:*:85:82:QMail user:/var/qmail:/nonexistent
qmails:*:87:82:QMail user:/var/qmail:/nonexistent
qmailp:*:84:81:QMail user:/var/qmail:/nonexistent
qmailr:*:86:82:QMail user:/var/qmail:/nonexistent
msql:*:87:87:mSQL-2 pseudo-user:/var/db/msqldb:/bin/sh
mysql:*:88:88:MySQL Daemon:/var/db/mysql:/sbin/nologin
vpopmail:*:89:89::0:0:User &:/usr/local/vpopmail:/nonexistent
Please include a notice when you submit a port (or an upgrade)
that reserves a new UID or GID in this range. This allows us to
keep the list of reserved IDs up to date.
Do things rationally
The Makefile should do things simply and
reasonably. If you can make it a couple of lines shorter or more
readable, then do so. Examples include using a make
.if construct instead of a shell
if construct, not redefining
do-extract if you can redefine
EXTRACT* instead, and using
GNU_CONFIGURE instead of CONFIGURE_ARGS
+= --prefix=${PREFIX} .
Respect CFLAGS
The port should respect the CFLAGS variable.
If it does not, please add NO_PACKAGE=ignores
cflags to the Makefile .
An example of a Makefile respecting
the CFLAGS variable follows. Note the
+= :
CFLAGS += -Wall -Werror
Here is an example which does not respect the
CFLAGS variable:
CFLAGS = -Wall -Werror
The CFLAGS variable is defined on
FreeBSD systems in /etc/make.conf . The
first example appends additional flags to the
CFLAGS variable, preserving any system-wide
definitions. The second example clobbers anything previously
defined.
Configuration files
If your port requires some configuration files in
PREFIX /etc , do
not just install them and list them in
pkg-plist . That will cause
pkg_delete to delete files carefully edited by
the user and a new installation to wipe them out.
Instead, install sample files with a suffix
(filename .sample
will work well) and print out a message pointing out that the
user has to copy and edit the file before the software can be made
to work.
Portlint
Do check your work with portlint
before you submit or commit it.
Feedback
Do send applicable changes/patches to the original
author/maintainer for inclusion in next release of the code. This
will only make your job that much easier for the next
release.
README.html
Do not include the README.html file. This
file is not part of the cvs collection but is generated using the
make readme command.
Miscellanea
The files pkg-comment ,
pkg-descr , and pkg-plist
should each be double-checked. If you are reviewing a port and feel
they can be worded better, do so.
Do not copy more copies of the GNU General Public License into
our system, please.
Please be careful to note any legal issues! Do not let us
illegally distribute software!
If you are stuck…
Do look at existing examples and the
bsd.port.mk file before asking us questions!
;-)
Do ask us questions if you have any trouble! Do not just beat
your head against a wall! :-)
A Sample Makefile
Here is a sample Makefile that you can use to
create a new port. Make sure you remove all the extra comments (ones
between brackets)!
It is recommended that you follow this format (ordering of
variables, empty lines between sections, etc.). This format is
designed so that the most important information is easy to locate. We
recommend that you use portlint to check the
Makefile .
[the header...just to make it easier for us to identify the ports.]
# New ports collection makefile for: xdvi
[the "version required" line is only needed when the PORTVERSION
variable is not specific enough to describe the port.]
# Date created: 26 May 1995
[this is the person who did the original port to FreeBSD, in particular, the
person who wrote the first version of this Makefile. Remember, this should
not be changed when upgrading the port later.]
# Whom: Satoshi Asami <asami@FreeBSD.org>
#
# $FreeBSD$
[ ^^^^^^^^^ This will be automatically replaced with RCS ID string by CVS
when it is committed to our repository. If upgrading a port, do not alter
this line back to "$FreeBSD$". CVS deals with it automatically.]
#
[section to describe the port itself and the master site - PORTNAME
and PORTVERSION are always first, followed by CATEGORIES,
and then MASTER_SITES, which can be followed by MASTER_SITE_SUBDIR.
PKGNAMEPREFIX and PKGNAMESUFFIX, if needed, will be after that.
Then comes DISTNAME, EXTRACT_SUFX and/or DISTFILES, and then
EXTRACT_ONLY, as necessary.]
PORTNAME= xdvi
PORTVERSION= 18.2
CATEGORIES= print
[do not forget the trailing slash ("/")!
if you are not using MASTER_SITE_* macros]
MASTER_SITES= ${MASTER_SITE_XCONTRIB}
MASTER_SITE_SUBDIR= applications
PKGNAMEPREFIX= ja-
DISTNAME= xdvi-pl18
[set this if the source is not in the standard ".tar.gz" form]
EXTRACT_SUFX= .tar.Z
[section for distributed patches -- can be empty]
PATCH_SITES= ftp://ftp.sra.co.jp/pub/X11/japanese/
PATCHFILES= xdvi-18.patch1.gz xdvi-18.patch2.gz
[maintainer; *mandatory*! This is the person (preferably with commit
privileges) whom a user can contact for questions and bug reports - this
person should be the porter or someone who can forward questions to the
original porter reasonably promptly. If you really do not want to have
your address here, set it to "ports@FreeBSD.org".]
MAINTAINER= asami@FreeBSD.org
[dependencies -- can be empty]
RUN_DEPENDS= gs:${PORTSDIR}/print/ghostscript
LIB_DEPENDS= Xpm.5:${PORTSDIR}/graphics/xpm
[this section is for other standard bsd.port.mk variables that do not
belong to any of the above]
[If it asks questions during configure, build, install...]
IS_INTERACTIVE= yes
[If it extracts to a directory other than ${DISTNAME}...]
WRKSRC= ${WRKDIR}/xdvi-new
[If the distributed patches were not made relative to ${WRKSRC}, you
may need to tweak this]
PATCH_DIST_STRIP= -p1
[If it requires a "configure" script generated by GNU autoconf to be run]
GNU_CONFIGURE= yes
[If it requires GNU make, not /usr/bin/make, to build...]
USE_GMAKE= yes
[If it is an X application and requires "xmkmf -a" to be run...]
USE_IMAKE= yes
[et cetera.]
[non-standard variables to be used in the rules below]
MY_FAVORITE_RESPONSE= "yeah, right"
[then the special rules, in the order they are called]
pre-fetch:
i go fetch something, yeah
post-patch:
i need to do something after patch, great
pre-install:
and then some more stuff before installing, wow
[and then the epilogue]
.include <bsd.port.mk>
Automated package list creation
First, make sure your port is almost complete, with only
pkg-plist missing. Create an empty
pkg-plist .
&prompt.root; touch pkg-plist
Next, create a new set of directories which your port can be
installed, and install any dependencies.
&prompt.root; mtree -U -f /etc/mtree/BSD.local.dist -d -e -p /var/tmp/port-name
&prompt.root; make depends PREFIX=/var/tmp/port-name
Store the directory structure in a new file.
&prompt.root; (cd /var/tmp/port-name && find * -type d) > OLD-DIRS
If your port honors PREFIX (which it should)
you can then install the port and create the package list.
&prompt.root; make install PREFIX=/var/tmp/port-name
&prompt.root; (cd /var/tmp/port-name && find * \! -type d) > pkg-plist
You must also add any newly created directories to the packing
list.
&prompt.root; (cd /var/tmp/port-name && find * -type d) | comm -13 OLD-DIRS - | sed -e 's#^#@dirrm #' >> pkg-plist
Finally, you need to tidy up the packing list by hand; it isn't
all automated. Manual pages should be listed in
the port's Makefile under
MANn , and not in the
package list. User configuration files should be removed, or
installed as
filename .sample .
The info/dir file should not be listed
and appropriate install-info lines should
be added as noted in the info
files section. Any
libraries installed by the port should be listed as specified in the
shared libraries section.
Package Names
The following are the conventions you should follow in naming your
packages. This is to have our package directory easy to scan, as
there are already lots and lots of packages and users are going to
turn away if they hurt their eyes!
The package name should look like
language_region -name- compiled.specifics -version.numbers .
The package name is defined as
${PKGNAMEPREFIX}${PORTNAME}${PKGNAMESUFFIX}-${PORTVERSION} .
Make sure to set the variables to conform to that format.
FreeBSD strives to support the native language of its users.
The language- part should be a two
letter abbreviation of the natural language defined by ISO-639 if
the port is specific to a certain language. Examples are
ja for Japanese, ru for
Russian, vi for Vietnamese,
zh for Chinese, ko for
Korean and de for German.
If the port is specific to a certain region within the
language area, add the two letter country code as well.
Examples are en_US for US English and
fr_CH for Swiss French.
The language- part should
be set in the PKGNAMEPREFIX variable.
The first letter of name part
should be lowercase. (The rest of the name can contain
capital letters, so use your own discretion when you are
converting a software name that has some capital letters in it.)
There is a tradition of naming Perl 5 modules by
prepending p5- and converting the double-colon
separator to a hyphen; for example, the
Data::Dumper module becomes
p5-Data-Dumper . If the software in question
has numbers, hyphens, or underscores in its name, you may include
them as well (like kinput2 ).
If the port can be built with different hardcoded defaults (usually
part of the directory name in a family of ports), the
-compiled.specifics part should state
the compiled-in defaults (the hyphen is optional). Examples are
papersize and font units.
The compiled.specifics part
should be set in the PKGNAMESUFFIX
variable.
The version string should follow a dash
(- ) and be a period-separated list of
integers and single lowercase alphabetics. In particular,
it is not permissible to have another dash inside the
version string. The only exception is the string
pl (meaning `patchlevel'), which can be
used only when there are no major and
minor version numbers in the software. If the software
version has strings like "alpha", "beta", "rc", or "pre", take
the first letter and put it immediately after a period.
If the version string continues after those names, the
numbers should follow the single alphabet without an extra
period between them.
The idea is to make it easier to sort ports by looking
at the version string. In particular, make sure version
number components are always delimited by a period, and
if the date is part of the string, use the
yyyy .mm .dd
format, not
dd .mm .yyyy
or the non-Y2K compliant
yy .mm .dd
format.
Here are some (real) examples on how to convert the name
as called by the software authors to a suitable package
name:
Distribution Name
PKGNAMEPREFIX
PORTNAME
PKGNAMESUFFIX
PORTVERSION
Reason
mule-2.2.2
(empty)
mule
(empty)
2.2.2
No changes required
XFree86-3.3.6
(empty)
XFree86
(empty)
3.3.6
No changes required
EmiClock-1.0.2
(empty)
emiclock
(empty)
1.0.2
No uppercase names for single programs
rdist-1.3alpha
(empty)
rdist
(empty)
1.3.a
No strings like alpha
allowed
es-0.9-beta1
(empty)
es
(empty)
0.9.b1
No strings like beta
allowed
mailman-2.0rc3
(empty)
mailman
(empty)
2.0.r3
No strings like rc
allowed
v3.3beta021.src
(empty)
tiff
(empty)
3.3
What the heck was that anyway?
tvtwm
(empty)
tvtwm
(empty)
pl11
Version string always required
piewm
(empty)
piewm
(empty)
1.0
Version string always required
xvgr-2.10pl1
(empty)
xvgr
(empty)
2.10.1
pl allowed only when no
major/minor version numbers
gawk-2.15.6
ja-
gawk
(empty)
2.15.6
Japanese language version
psutils-1.13
(empty)
psutils
-letter
1.13
Papersize hardcoded at package build time
pkfonts
(empty)
pkfonts
300
1.0
Package for 300dpi fonts
If there is absolutely no trace of version information in the
original source and it is unlikely that the original author will ever
release another version, just set the version string to
1.0 (like the piewm example above). Otherwise, ask
the original author or use the date string
(yyyy .mm .dd )
as the version.
Categories
As you already know, ports are classified in several categories.
But for this to work, it is important that porters and users understand
what each category is for and how we decide what to put in each
category.
Current list of categories
First, this is the current list of port categories. Those
marked with an asterisk (* ) are
virtual categories—those that do not have
a corresponding subdirectory in the ports tree.
For non-virtual categories, you will find a one-line
description in the pkg/COMMENT file in that
subdirectory (e.g.,
archivers/pkg/COMMENT ).
Category
Description
afterstep*
Ports to support the AfterStep window manager.
archivers
Archiving tools.
astro
Astronomical ports.
audio
Sound support.
benchmarks
Benchmarking utilities.
biology
Biology-related software.
cad
Computer aided design tools.
chinese
Chinese language support.
comms
Communication software. Mostly software to talk to
your serial port.
converters
Character code converters.
databases
Databases.
deskutils
Things that used to be on the desktop before
computers were invented.
devel
Development utilities. Do not put libraries here just
because they are libraries—unless they truly do not
belong anywhere else, they should not be in this
category.
editors
General editors. Specialized editors go in the section
for those tools (e.g., a mathematical-formula editor will go
in math ).
elisp*
Emacs-lisp ports.
emulators
Emulators for other operating systems. Terminal
emulators do not belong
here—X-based ones should go to
x11 and text-based ones to either
comms or misc ,
depending on the exact functionality.
french
French language support.
ftp
FTP client and server utilities. If your
port speaks both FTP and HTTP, put it in
ftp with a secondary
category of www .
games
Games.
german
German language support.
gnome*
Ports from the GNU Object Model Environment (GNOME)
Project.
graphics
Graphics utilities.
hebrew
Hebrew language support.
irc
Internet Relay Chat utilities.
ipv6*
IPv6 related software.
japanese
Japanese language support.
java
Java language support.
kde*
Ports from the K Desktop Environment (KDE)
Project.
korean
Korean language support.
lang
Programming languages.
linux*
Linux applications and support utilities.
mail
Mail software.
math
Numerical computation software and other utilities
for mathematics.
mbone
MBone applications.
misc
Miscellaneous utilities—basically things that
do not belong anywhere else. This is the only category
that should not appear with any other non-virtual category.
If you have misc with something else in
your CATEGORIES line, that means you can
safely delete misc and just put the port
in that other subdirectory!
net
Miscellaneous networking software.
news
USENET news software.
offix*
Ports from the OffiX suite.
palm
Software support for the 3Com Palm(tm) series.
perl5*
Ports that require perl version 5 to run.
picobsd
Ports to support PicoBSD.
plan9*
Various programs from Plan9.
print
Printing software. Desktop publishing tools
(previewers, etc.) belong here too.
python*
Software written in python.
ruby*
Software written in ruby.
russian
Russian language support.
science
Scientific ports that don't fit into other
categories such as astro ,
biology and
math .
security
Security utilities.
shells
Command line shells.
sysutils
System utilities.
tcl76*
Ports that use Tcl version 7.6 to run.
tcl80*
Ports that use Tcl version 8.0 to run.
tcl81*
Ports that use Tcl version 8.1 to run.
tcl82*
Ports that use Tcl version 8.2 to run.
textproc
Text processing utilities. It does not include
desktop publishing tools, which go to print/.
tk42*
Ports that use Tk version 4.2 to run.
tk80*
Ports that use Tk version 8.0 to run.
tk81*
Ports that use Tk version 8.1 to run.
tk82*
Ports that use Tk version 8.2 to run.
tkstep80*
Ports that use TkSTEP version 8.0 to run.
ukrainian
Ukrainian language support.
vietnamese
Vietnamese language support.
windowmaker*
Ports to support the WindowMaker window
manager
www
Software related to the World Wide Web. HTML language
support belongs here too.
x11
The X window system and friends. This category is only
for software that directly supports the window system. Do not
put regular X applications here. If your port is an X
application, define USE_XLIB (implied by
USE_IMAKE ) and put it in the appropriate
categories. Also, many of them go into other
x11-* categories (see below).
x11-clocks
X11 clocks.
x11-fm
X11 file managers.
x11-fonts
X11 fonts and font utilities.
x11-servers
X11 servers.
x11-toolkits
X11 toolkits.
x11-wm
X11 window managers.
zope*
Zope support.
Choosing the right category
As many of the categories overlap, you often have to choose
which of the categories should be the primary category of your port.
There are several rules that govern this issue. Here is the list of
priorities, in decreasing order of precedence.
Language specific categories always come first. For
example, if your port installs Japanese X11 fonts, then your
CATEGORIES line would read japanese
x11-fonts .
Specific categories win over less-specific ones. For
instance, an HTML editor should be listed as www
editors , not the other way around. Also, you do not
need to list net when the port belongs to
any of irc , mail ,
mbone , news ,
security , or www .
x11 is used as a secondary category only
when the primary category is a natural language. In particular,
you should not put x11 in the category line
for X applications.
Emacs modes should be
placed in the same ports category as the application
supported by the mode, not in
editors . For example, an
Emacs mode to edit source
files of some programming language should go into
lang .
If your port truly does not belong anywhere else, put it in
misc .
If you are not sure about the category, please put a comment to
that effect in your send-pr submission so we can
discuss it before we import it. If you are a committer, send a note
to the &a.ports; so we can discuss it first—too often new ports are
imported to the wrong category only to be moved right away.
Changes to this document and the ports system
If you maintain a lot of ports, you should consider following the
&a.ports;. Important changes to the way ports work will be announced
there. You can always find more detailed information on the latest
changes by looking at the
bsd.port.mk CVS log .
That is It, Folks!
Boy, this sure was a long tutorial, wasn't it? Thanks for
following us to here, really. Now that you know how to do a port,
have at it and convert everything in the world into ports! That
is the easiest way to start contributing to the FreeBSD Project!
:-)