Some old-school UNIX shell hackery using “tr” for a UNIX v6 kernel build

I had to resort to some old-school UNIX shell hackery to get V6 UNIX running in SIMH as an automated process.

If you refer to the earlier posts, you’ll see that there were three main steps in getting a well-running UNIX kernel with new device drivers created:

  1. Boot from “tape” and copy an image of the root filesystem onto a simulated RK disk
  2. Boot from the RK disk, modify source code for several programs such as “df” and the kernel. Compile the new kernel, copy it into place in the root filesystem.
  3. Boot the new kernel from the new root filesystem.

This post covers step 2, which involves creating and editing several files to include information about new device drivers, then then compiling the new kernel. The entire process is very well documented at: https://gunkies.org/wiki/Installing_Unix_v6_(PDP-11)_on_SIMH so I won’t cover it in detail.

Recall that while we have a running V6 system, there’s no way to copy files into the virtual machine. We can enter shell commands, but not copy files into the emulated system. This means that we have to play games with “cat”, “ed” and other UNIX commands to create new files or edit existing ones. There’s a bit of a complication in that to enter commands into UNIX running within the SIMH emulator, we have to use the SIMH EXPECT/SEND commands. These are documented in the SIMH user’s guide for Version 4.

Also, remember that this is the 1975 “sh” shell. It’s very primitive. If we had the modern “here document” feature, this would have been much simpler. I’m not sure, but I believe that feature didn’t appear until 4BSD (1980). Instead we’ll have to rely on the commands that were there 44 years ago.

For example, this set of commands would normally just be entered on the command line.

chdir /usr/sys/conf
cc mkconf.c
mv a.out mkconf

But to use the EXPECT command, it looks like this, using “;” as the command separator to do this all on a single input line:

expect '#' send 'chdir /usr/sys/conf; cc mkconf.c; mv a.out mkconf\r'; continue

So far so good, but what about something more complex, where we’re even farther down the rabbit hole? Passing input to a program that’s running in the shell, in the emulated OS is one step further in and requires some “old school” shell hackery. For example, what about sending input into the “mkconf” program:

# ./mkconf
rk
tm
tc
8dc
lp
done
#

It turns out that we can’t just feed this in as multiple EXPECT/SEND combinations due to the need for embedded newlines. Any newlines in the EXPECT script would end the line, any newlines embedded in the SEND strings would also be lost.

This was a head scratcher for an hour, until I remembered some similar problems I’d had years ago doing stream editing (sed) to patch binary program files on the fly instead of re-compiling the source (don’t ask, ugly).

This led me to using the “tr” program to “send” newlines without ever using the newline character in my command.

expect '#' send 'echo \'rkXtmXtcX8dcXlpXdone\' | tr X \\\\012 | mkconf\r'; continue

You can see that the newline character never appears anywhere in the SEND string. The echo command will emit the needed lines, but with X instead of newline. The tr command will replace the X character with an escaped-and-escaped-again 012, the octal for newline. This feeds 6 newline separated strings (lines) into the mkconf program, without ever actually using a newline!

After that, it was back to vanilla scripting, until I hit another similar glitch. To add the RK disk to the list of supported devices in the “df” command, you need to edit the source to add 2 lines into an array that lists the supported devices. Interactively, this is pretty trivial (if you know “ed”).

# chdir /usr/source/s1
# ed df.c
/rp0/d
.-2a
  "/dev/rk0",
  "/dev/rk1",
.
w
q
# cc -s -O df.c
# cp a.out /bin/df
# rm a.out

And here we are again, needing to enter multiple lines to a program, without using the newline character. It’s “tr” to the rescue again:

expect '#' send 'chdir /usr/source/s1 ; echo \'/rp0/dX.-2aX  "/dev/rk0",X  "/dev/rk1",X.XwXqX\' | tr X \\\\012 | ed df.c\r' ; continue

And finally, one last time:

# ed /etc/ttys
1,8s/^0/1/p
w
q
# 

becomes

expect '#' send 'echo \'1,8s/^0/1/pXwXqX\' | tr X \\\\012 | ed /etc/ttys\r' ; continue

You can find all this hackery (and some other uglier things) in the “buildunix.ini file in the github repository.

For me, this was a fun trip down memory lane and the weird things we had to do, back when computers were more primitive and yet sometimes more fun.

, , , ,

%d bloggers like this: