Monthly Columns
 

Getting started with RCS

Copyright © 1999 Sue Blake

This article is aimed at the BSD user who is just beginning to learn about working with Unix. Its subject matter is of interest to BSD users at all levels. Confident newbies are encouraged to read quickly and move to the man pages when comfort sets in.

If you've ever lost an important configuration file, or lost track of what changes were made before some trouble started, you already know you need something like RCS. It stands for Revision Control System. If you've heard of CVS, perhaps in an earlier article, don't be deterred by the obvious similarities. CVS is based on RCS but far more complex than what you need for this purpose. Even RCS offers more than you need to keep track of important configuration files, but stick to the basics and it's very easy to learn.

RCS can keep a history of all changes made to a file, and the reasons for those changes, recreate any previous version, list the differences, and much more. When you back up an RCS file, only slightly larger than the regular file, you're backing up the whole history of that file and its changes. If you're learning about BSD, RCS offers a way of looking back at what you have done and learned in the past.

It's hard to understand what RCS really is until you've used it, and hard to use before you understand what it is and how it works. The solution is to try it as you read about it. which is the approach we're using here.

The man pages (yes there are several to read) don't make a lot of sense until you know how to discard what you don't need, and for that you have to have seen RCS in action to decide how you're going to use it. Don't start with 'man rcs' because you might never need that particular command and you'll need some experience to get the most out of it. Start with 'man rcsintro' and be very selective the first time you read through it.

This article presents the minimum you need to start benefiting from using RCS. It's not much to learn, just one command to type every time you edit a precious config file. Once you've started actually doing it we can go on to examine what is happening, and how to access more of what RCS has to offer by selectively using its many man pages which will begin to make sense. We'll take it in tiny steps so that even people with very little Unix experience can follow. Others, read quicker.

First we look at the ultra simple approach, just one command. That produces some RCS info to look at, which should make the process clearer. Then we'll investigate the two command approach that is the more standard entry point. Last we'll look at a few other commands that are useful occasionally but lie buried among dozens in the manuals.


  1. Quick and dirty RCS
  2. What's here now?
  3. Why freedom requires locks
  4. RCS to the rescue


Quick and dirty RCS

Here's how it works. Each time you edit an important file, such as rc.conf or the kernel config file, you run a special command afterwards. This causes RCS to stash your latest changes into its own special file which it looks for in a subdirectory called RCS. Then RCS asks for a brief note (log entry) about what you've changed this time, and optionally, why. You might expect the first time to be special, requiring a different command to register the file with RCS. Theoretically that is the case, but there's no need to. You can use the same command that you would use to check in changes to an existing file. Instead of asking about the changes, RCS will see that it's a new file, set it up, and just ask for a description of what the file is. Clever, huh?

That's it.

Then one day in the future when the system goes berzerk and someone (possibly yourself) points the finger demanding "You must have done something wrong, you idiot! What did you change?" you can smile and calmly produce your RCS logs, saying "nothing that would have caused this trouble, but why don't you take a look?" Oh, but you do mangle config files occasionally? Not to worry, you can pull out a fresh copy of the last or any previous version. If you've been following the articles about using floppy disks for tiny frequent backups of precious files, the files in RCS directories are good candidates. Then if you ever get carried away and wipe your whole disk, you'll still have some previous configuration history to refer to.

OK, now it's time to see it in action. Roll up your sleeves and actually do this. Instead of working on important files as root right away, you could try similar examples as a normal user first, using a rubbish file in your personal home directory.

In the first issue of Daemon News we saw how to improve security by simply editing inetd.conf to remove any services that aren't being used. That will be our example task. If you don't want to change that file, don't worry, you can recreate the original version later. OK, since this is your first time with RCS, better make a spare copy of the original just in case :-) Since we'll only be adding and removing comments, you can follow this through even if you're still learning to use a text editor.

First su to root, and cd to the /etc directory where our victim, inetd.conf, is found. Make a directory there called RCS so that RCS has a place for its private files of mumbo jumbo instead of dropping these peculiar files all over /etc. We're not supposed to be interested in what's in the RCS directory, and if those files get changed manually it will muck everything up, so keep out of there.


% su
# cd /etc
# ls inet*
inetd.conf
# mkdir RCS
#

Here's the RCS command that you'll be using over and over:
ci -l inetd.conf

Note that the letter after the hyphen is a lower case L, and the name of whatever file you're working on would go in place of inetd.conf.

ci means "check in". At the beginning you check in the original version, and after each edit you check in the latest changes. RCS looks at the file and knows what to do.

-l is important. It tells RCS to "lock" the file so that you can edit it again. At this stage locking might sound like the opposite of what it does, so just remember you must use the -l for now and we'll look at what this really means later on.

Use that command to check in the original copy of inetd.conf. You will still have /etc/inetd.conf but RCS will create a record of its contents in the RCS directory, in a file called inetd.conf,v (yes file names can contain commas!). Because it's the first time for this file, RCS wants a description of it. Everything below comes from RCS except the initial command and the text to the right of the ">> ", including the important lonely dot at the end of the description.


# ci -l inetd.conf
RCS/inetd.conf,v  <--  inetd.conf
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>> Configuration file for inetd(8)
>> inetd is run at booot time and listens for connections on certain sockets.
>> These types of connections can be enabled or disabled by (un)commenting
>> their entries in this config file.
>> .
initial revision: 1.1
done

Where did that nice description come from? I made it up, stealing heavily from the man page. There's a small typo but it really doesn't matter so long as I can understand it. Some of my descriptions have been more like "I haven't got a clue what this file is for but it has something to do with foo and I have been advised to edit it to get foo working" and my future log entries will show the penny gradually dropping.

RCS now has a description of the file and a record of the file's original contents. Next, use your preferred text editor to comment out ftp and telnet as suggested in the security article.

The lines
 ftp	stream	tcp	nowait	root	/usr/libexec/ftpd	ftpd -l
 telnet	stream	tcp	nowait	root	/usr/libexec/telnetd	telnetd
will become
 #ftp	stream	tcp	nowait	root	/usr/libexec/ftpd	ftpd -l
 #telnet	stream	tcp	nowait	root	/usr/libexec/telnetd	telnetd

Save the file, and check in the changes. You will be asked for a log message stating what was changed, but it will be much more useful later on if you say why you made the changes too.


# ci -l inetd.conf
RCS/inetd.conf,v  <--  inetd.conf
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> Disabled ftp and telnet services as suggested in the security article
>> at http://www.daemonnews.org/199809/security.html
>> I can use ssh instead of these, but if I ever do need them enabled I can
>> simply uncomment the two lines and restart inetd.
>> .
done

Now RCS knows all about the original file and the changed version.

Let's do that again. Edit inetd.conf to comment out the finger service then check in the changes:


# ci -l inetd.conf
RCS/inetd.conf,v  <--  inetd.conf
new revision: 1.3; previous revision: 1.2
enter log message, terminated with single '.' or end of file:
>> Disabled finger. Supposedly a security risk, I'm not sure
>> how that is so, but I don't need finger so why have it.
>> .
done
#

Now RCS has, in its own directory, a file in a special format that tells RCS

1. a description of the file
2. the contents of the original file

3. the contents of the file after the first changes were made
4. what those changes were and why they were made (the log message)
5. which user made those changes and when (in GMT)

6. the contents of the file after the second changes were made
7. what those changes were and why they were made
8. which user made those changes and when

and so it continues as more changes are checked in over time. (RCS maintains some other information about the file, such as version numbers, that we don't have to bother with to do this basic task.)

If you want to keep following through, refer to the security article and make a couple more edits, checking each one in to RCS. I put telnet back, and later on I disabled shell. You might have different requirements. Remember that inetd won't follow your changes until you either restart inetd or reboot the computer. Don't think you're safe if you don't restart inetd. Murphy's law says that the cat will trip over the power cable causing an involuntary reboot at the precise moment that it can cause most damage.


What's here now?

I'm going to cheat here and use two other commands to help me show you what's happening. You don't have to learn them yet. You'll find them all listed in the rcsintro man page under "SEE ALSO". First I want to show you how RCS has retained and displays all of the log information. The file's description is at the top, but the rest should be read from the bottom up.


# rlog inetd.conf

RCS file: RCS/inetd.conf,v
Working file: inetd.conf
head: 1.4
branch:
locks: strict
	root: 1.4
access list:
symbolic names:
keyword substitution: kv
total revisions: 4;	selected revisions: 4
description:
Configuration file for inetd(8)
inetd is run at booot time and listens for connections on certain sockets.
These types of connections can be enabled or disabled by (un)commenting
their entries in this config file.
----------------------------
revision 1.4	locked by: root;
date: 1999/05/14 18:17:27;  author: root;  state: Exp;  lines: +1 -1
telnet enabled again until I get ssh installed
----------------------------
revision 1.3
date: 1999/05/14 18:11:32;  author: root;  state: Exp;  lines: +1 -1
Disabled finger. Supposedly a security risk, I'm not sure
how that is so, but I don't need finger so why have it.
----------------------------
revision 1.2
date: 1999/05/14 18:08:56;  author: root;  state: Exp;  lines: +2 -2
Disabled ftp and telnet services as suggested in the security article
at http://www.daemonnews.org/199809/security.html
I can use ssh instead of these, but if I ever do need them enabled I can
simply uncomment the two lines and restart inetd.
----------------------------
revision 1.1
date: 1999/05/10 06:13:33;  author: root;  state: Exp;
Initial revision
----------------------------

Hey, I distinctly remember disabling shell! Where is it? Did I forget to check in the last change? Oh dear, I can't remember what other changes have I made lately. Their commented history is lost! How can tell RCS the truth when I check the file in?

Relax, that's what RCS is for, and why you'll eventually find the man pages useful. Watch this:


# rcsdiff inetd.conf
===================================================================
RCS file: RCS/inetd.conf,v
retrieving revision 1.4
diff -r1.4 inetd.conf
8c8
< shell	stream	tcp	nowait	root	/usr/libexec/rshd	rshd
---
> #shell	stream	tcp	nowait	root	/usr/libexec/rshd	rshd
#

RCS compares the latest revision it knows about, which it calls 1.4 here, with the inetd.conf file sitting in the current directory. The < marks lines that appear in RCS's latest revision, and the > marks lines that appear in the live file. Only the lines that differ are shown. So it's easy to see that the only change I still have to check in is the one that commented out shell. Simple!

Now you'd better find out about this locking business, because if you ever get it wrong you could end up with a file with the wrong permissions to do its job, or no file there at all until you check out another copy.


Why freedom requires locks

In order to see what "locking" means to RCS, let's look at the man page rcsintro(1). It describes the more well known two-command method of using RCS, and explains a little more that we don't need right now, then refers you to related man pages.

If you're working on a file that doesn't need to be available at all times, you can use the plain ci (check in) command without the -l. Then after the file is checked in, you no longer have a working copy. Next time you want to see it you use the co (check out) command to get a copy. This copy will be read-only, and that's fine if it's not a file that the system needs access to and you just want to look and not risk mucking it up.

But if you want to check out a file to edit it, you have to "lock" it when you check it out. Why lock it? RCS is prepared for many users to be working on the one project. Locking is necessary so that if two or more users want to edit the same file simultaneously, RCS can see what's going on and help to prevent any conflicts. The command is co -l filename when you want to edit the latest revision of the file. The man page co(1) shows how to check out other revisions.

So a common two-command method uses ci filename followed immediately by co -l filename. In the first section above, we've simplified this by locking when checking the file in (ci -l filename). This single command is approximately equivalent to a check in followed immediately by a locked check out, and means that the working file is never unavailable, and has its original permissions unchanged. The file is locked all the time. That means it is editable by anyone who the permissions allow to do so, but can only be checked in by the person who has locked it. Apart from being easier to remember, this single command method is more suitable for configuration files that are edited by root and must be checked out at all times.


RCS to the rescue

There is a lot more you can do with RCS, but how much do you actually need? To get started, and for routine use, not much at all.

We've seen the simplest use of rlog and rcsdiff above, which is probably all you'll need. By the time you start using RCS in different ways, you'll need to be referring to the man pages, but by then they will be a lot easier to understand. For example, you might discover that another nice way to use rlog is rlog -r filename which shows the file description and just the last log entry. You can use rcsdiff to see the differences between any two revisions, which is explained in its man page. As an example of using the "diff options" that the man page refers to, try using rcsdiff -wu filename to make the output of differences easier to read.

Let's finish by looking at a couple of issues that you might need to deal with before reaching the more inquisitive stages.

Help! I want the original file back!

Check out the lowest revision number, which by default is 1.1.
co -l1.1 filename

Oops, I shouldn't have made the last set of checked in changes

You can use rlog to see what changes went into each revision, then check out the desired revision number. For example, to get a copy of revision 1.8 for editing:
co -l1.8 filename
or if you just want to have a quick look, use -d to send a copy of the file's contents to standard output. For example:
co -d1.8 filename | more

How can I show the revision number inside the file?

Type $Id$ somewhere inside a comment in the file, and it will be expanded to show revision information every time the file is checked out. Don't edit the string that it produces, and RCS will take care of it at each update. For shell scripts (their first line is #!/bin/sh), lines beginning with # are comments. For web pages, you can insert it like this <!-- $Id$ --> and it won't show. For other types of files, check carefully to see how the comments are marked. Don't put your ident string somewhere in a configuration file where it will confuse the program that reads the file. Some files, including many configuration files, have no safe place to include an ident string. It's nice to have, but you'll get by fine without it.

But there's a better way!

Sure, nothing in this article is supposed to be the "best" way, but it works, and it's easy to remember. You'll be missing a lot if you don't read more about RCS. The point is that you don't have to study hard or execute commands you don't understand in order to keep track of your configuration changes, and you can start right from day one, even if you've never used BSD before.

The man pages you might find most interesting include: rcsintro(1), ci(1), co(1), rlog(1), rcsdiff(1), diff(1). For later on there's rcs(1), ident(1), rcsfile(5), rcsmerge(1), rcsclean(1).

Sue Blake, sue@welearn.com.au