Revisiting a UnixWare 7.1.1 install on Qemu/KVM

So after nearly 8 years ago from messing around with UnixWare, I wanted to confirm something from a SYSV Unix that has a C compiler that isn’t GCC, and I remembered I have UnixWare 7.1.1 from a long time ago.  Anyways I have long since lost the virtual machine I had installed onto, but I still have media and of course the more important licenses.

unixware certificate of license and authenticity

UnixWare licenses. the dread of fixing things 20+ years later

Yep it’s the real thing.  So with my certs in hand I do an initial install in Qemu and on reboot the system basically has bricked itself.

WARNING: System is in an unreliable state.

And then looking at the licenses it turns out that my license has expired.  What?

Somehow I got lucky before, but it turns out that the installation process for ancient UnixWare is NOT Y2K compliant!  And this actually turned out to be a known issue.  I can’t find the original article, but a mirror is here: ischo.net

So basically install using an eval license, which will of course expire on install, and then use your actual license after the installation, remove the eval, reboot and all will be well.

License Number: UW711EVAL
License Code: airhorpx
License Data: d60;m7hjbtt

Now isn’t that great.

The OS install license immediately expires.

Although you can’t boot up in any real useful state, the networking will kick people off, and it’ll constantly complain that you are in license violation, you can at least bring up the SCO Admin tool, and add in your actual licenses, and then delete the evals.

And now we’re good!

Ok, now for the real fun part, flags and how to run with kvm/qemu.  Since I was loading this onto a server for remote access something like this works fine for me as I’m using the VNC remote console.

qemu-system-x86_64 -enable-kvm -m 1024 -smp 4 -hda UnixWare.vmdk -cpu pentium -net none -monitor telnet::444,server,nowait -curses -vnc 10.12.0.1:11 -cdrom iso/SCO_UnixWare711.iso

So the key things are to restrict the CPU level, and I’ve deferred the network configuration so I can update network drivers, and the OS.  I’ve found that by doing the networking during the install resulted in an OS that crashed with an integer divide by zero after installing the fixpack 5.

Once you have UnixWare 7.1.1 installed, be sure to install Maintenance Pack 5, which is thankfully still online over at sco.com  I’d also recommend to do this in single user mode, you can enter single user mode by hitting a key during the boot logo and typing in:

INITSTATE=S
boot

And you’ll boot in single user mode, and can install the Maintenance pack with ease.  Until the maintenance pack is installed, expect poor stability, and the system won’t actually listen to the real time clock device, and it’ll accelerate the clock like crazy where I was passing an hour every minute or two.

Adding the AMD PCnet on UnixWare

Once the install and update is done, I just added a PCI network card (So older versions of Qemu work well with the ne2k_isa, but newer work much better with the AMD PCNet card.), which is a popular choice for both machines and VM’s of the era.  Although you can use SLiRP the built in NAT for Qemu/KVM alternatively you can also use tun/tap.  I tried to enable SMP, however it has issues binding to the other processors, although it does see them.  And this is better to give full access to the network stack for fun things like FTP, NFS and whatnot.

qemu-system-x86_64 -enable-kvm -m 1024 -smp 4 -hda UnixWare.vmdk -cpu pentium -monitor telnet::444,server,nowait -curses -vnc 10.12.0.1:11 -device pcnet,netdev=net0 -netdev tap,id=net0,ifname=tap10,script=/etc/qemu-ifup

Telnet access

And just like that I can now access the VM.

And for fun…

# uname -a
UnixWare kvm711 5 7.1.1 i386 x86at SCO UNIX_SVR5
# uname -f
architecture=IA32
bus_types=PCI2.10,ISA,PnP1.0
hostname=kvm711.joes.local
hw_provider=Generic AT
hw_serial=DEM076116
kernel_stamp=04/11/11
machine=Pentium
num_cg=1
num_cpu=1
os_base=UNIX_SVR5
os_provider=SCO
release=5
srpc_domain=
sysname=UnixWare
user_limit=5
version=7.1.1

Oh yeah so I don’t forget years from now I’m using the following OS & Qemu version:

# qemu-system-x86_64 -version
QEMU emulator version 2.8.1(Debian 1:2.8+dfsg-6+deb9u3)

# cat /etc/issue
Debian GNU/Linux 9 \n \l

Also I found an eval serial for SMP, but it doesn’t recognize the second processor after the boot.

# psrinfo -v
Status of processor 0 as of 01/31/18 16:40:07
Processor has been on-line since 01/31/18 16:13:57.
The Pentium processor has a i387 floating point processor.
The following conditions exist:
Device drivers are bound to this processor.

I’ll try apparently this as for some reason it doesn’t detect the MP in Qemu/KVM so it never installed the osmp driver.

pkgadd -d cdrom1 osmp

Add the following line to the file /stand/boot:
ACPI=Y

Date and Time on UNIX V7

(This is a guest post by xorhash.)

Introduction

I’ve recently defeated one of my bigger inconveniences, broken DEL as backspace on the UNIX®† operating system, Seventh Edition (commonly known as UNIX V7 or just V7). However, I’ve had another pet peeve for a while: How much manual labor is involved in booting the system. Reader DOS found out that SIMH recently added support for SEND/EXPECT pairs to react to output from the simulator. Think of them like UUCP chat scripts, effectively. This can be used to automate the bootup procedure.

Yet DOS’s script skips over a part of the bootup procedure that can be fully automated with some additional tooling. Namely, setting the date/time to the current time as defined by the host system. As per boot(8), the operator is meant to set the date/time every time the system is brought up. This should be possible to automate, right?

Setting the Date Automatically

date(1) itself does not support setting years past 2000, so we need custom code in any case. SIMH, fortunately, also provides a way to get the current timestamp in the form of %UTIME%, which is interpreted in any argument to any command. I’ve thus written a utility called tsdate that takes a timestamp as argument and sets the current time to be that timestamp. I put the executable in /etc/tsdate, but there’s really no reason to do so other than not wanting to accidentally call it. Once tsdate is in place, changing DOS’s script slightly will already do the trick:

expect "\n\r# " send "/etc/tsdate %UTIME%\r\004"; c

This approach already has a minor amount of time drift ab initio, namely the difference between the actual time on the host system and the UNIX timestamp. In the worst case, this may be very close to 1. If for some reason you need higher accuracy than this, you’ll probably have a fairly hard time. I could imagine some kind of NTP-over-serial, but you’d need support for chat scripts to get past the authentication due to getty(8) spawning login(1).

The system is not suitable for usage past the year 2038. However, you can at least push it back until around 2100 by changing the internal representation of time to be an unsigned long instead of simply a long. time_t was not used systematically. Instead, everything assumed long as the type for times. This assumption is in a lot of places in userspace and even the man pages use long instead of time_t.

If you overflow tsdate’s timestamp, you’ll just get whatever happens when atol(3) overflows. There’s nothing in the standard library for parsing strings into unsigned long and the year 2038 is far enough away that I didn’t want to bother. stime(2) would presumably also need to be adjusted.

Year 2000 Compatibility in the System

V7 is surprisingly good at handling years past 2000. Most utilities can print years up to and including 2099 properly. Macros for nroff(1)/troff(1), however, are blissfully unaware that years past 1999 may exist. This causes man pages to be supposedly printed in the year 19118. The root cause for this is that the number register yr only holds the current year minus 1900. Patches to the -ms and -man macros are required. Similarly, refer(1) only considers years until 2000 to be actual years, though I did not bother patching that since it should only affect keyword matching.

Leap year handling is broken in two different places due to wrong leap year handling: at(1) and the undocumented dysize() function, used by date(1) as well as inside /usr/src/libc/gen/ctime.c for various purposes. This affects the standard library, so recompiling the entire userland is recommended. Because the calculation is just a naïve division by four, it actually works on the year 2000 itself. A year is a leap year, i.e. has February 29, if a year is:

  1. a multiple of 4, and
    1. not a multiple of 100, or
    2. a multiple of 400.

Leap seconds are also not accounted for, but that comes to nobody’s surprise. Leap seconds just add a second 60 to the usual 00-59, so they don’t hurt doing date calculations on timestamps unless precisely on a leap second. For my purposes, they can be ignored.

Note that I haven’t gone through the system with a fine-toothed comb. There can always be more subtle time/date issues remaining in the system. For my purposes, this works well enough. If other things crop up, you’re welcome to put them in the comments for future generations.

The Good Stuff

There’s really not much to it. Applying diff and recompilation of the affected parts is left as an exercise for the reader.

Files:

  1. tsdate.c
  2. tsdate.1m
  3. y2kpatches.diff

† UNIX is a trademark of The Open Group.

OpenBSD 5.5 released!

McFishy

McFishy

OpenBSD 5.5 was just released! And in case you don’t get the DeLorean reference, this release focuses on fixing the 2038 issue!

From the change list:

  • time_t is now 64 bits on all platforms.
    • From OpenBSD 5.5 onwards, OpenBSD is year 2038 ready and will run well beyond Tue Jan 19 03:14:07 2038 UTC.
    • The entire source tree (kernel, libraries, and userland programs) has been carefully and comprehensively audited to support 64-bit time_t.
    • Userland programs that were changed include arp(8), bgpd(8), calendar(8), cron(8), find(1), fsck_ffs(8), ifconfig(8), ksh(1), ld(1), ld.so(1), netstat(1), pfctl(8), ping(8), rtadvd(8), ssh(1), tar(1),tmux(1), top(1), and many others, including games!
    • Removed time_t from network, on-disk, and database formats.
    • Removed as many (time_t) casts as possible.
    • Format strings were converted to use %lld and (long long) casts.
    • Uses of timeval were converted to timespec where possible.
    • Parts of the system that could not use 64-bit time_t were converted to use unsigned 32-bit instead, so they are good till the year 2106.
    • Numerous ports throughout the ports tree received time_t fixes.

Wow, that’s pretty cool!

And of course for VMware users:

  • New vmx(4) driver for VMware VMXNET3 Virtual Interface Controller devices.
  • New vmwpvs(4) driver for VMware Paravirtual SCSI.
  • New vioscsi(4) driver for VirtIO SCSI adapters.
  • New viornd(4) driver for VirtIO random number devices.

In addition the new vxlan driver looks pretty interesting too!

As always get your copy from one of the many HTTP mirrors, and why not support the project with the purchase of a CD or poster?

free. functional and secure...

free. functional and secure…

The 2038 ‘problem’.

Eventually you will start to hear about the 2038 problem regarding UNIX, and the C language. Looking at the 4.3 BSD source, the problem is pretty simple.

The time_t value holds the number of ‘ticks’ since new years, 1970. It’s a signed long, that can hold a maximum value of 2147483648. This will set you into the year 2038. There is some great detail on the site 2038bug.com . Naturally the easiest option here, is to simply change it from a signed long to an unsigned long. This will let us use a maximum value of 4294967296, which translates to the date Sun Feb 6 06:28:15 2106.

While this is not a “perfect” solution for some people, this works great on platforms that are not built with compilers that can understand 64bit “long longs”. Not to mention that constantly updating a pseudo long long could have negative implications in the kernel….

The bottom line is that eventually we need to move away from 32bit platforms, and with some programs that try to look 30+ years into the future, the time is NOW. But just because you are on a 64bit UNIX doesn’t mean the date isn’t being kept in a 32bit signed value for ‘compatibility’…

To test the idea, I’ve taken the 4.3 UWisc BSD from my project here, and gone about making some changes.

Starting with the file /usr/sys/h/types.h

myname# diff -r types.h x
47c47
< typedef long time_t;

> typedef unsigned long time_t;

Very simple, right? The kernel has it’s own version of this file, /usr/sys/h/types.h , and will require the same fix.

myname# diff -r types.h x
47c47
< typedef long time_t;

> typedef unsigned long time_t;

Now with that out of the way, the next thing to do is patch some of the functions in libc.

/usr/src/lib/libc/gen/time.c

myname# diff time.c /time.c
25c25
< long

> unsigned long

/usr/src/lib/libc/gen/ctime.c

myname# diff ctime.c /ctime.c
248c248
< long hms, day;

> unsigned long hms, day;

Ok, that’s the bulk of the changes. Really.

Rebuild the kernel & libc, and install them.

cd /usr/sys/GENERIC
make clean
make
cp vmunix /

cd /usr/src/lib/libc
make
cp libc.a /lib
cp libc_p.a /usr/lib
ranlib /lib/libc.a
ranlib /usr/lib/libc_p.a

Now with that out of the way, reboot the VM.

So far everything should behave normally. But it’s time for some fun!

First, let’s make sure we can use dates beyond the 2038 bug date. This program comes from the aforementioned 2038bug.com site. I’ve just added one header needed by 4.3 BSD to get the time_t structure called in correctly.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>

int main (int argc, char **argv)
{
time_t t;
t = (time_t) 1000000000;
printf (“%d, %s”, (int) t, asctime (gmtime (&t)));
t = (time_t) (0x7FFFFFFF);
printf (“%d, %s”, (int) t, asctime (gmtime (&t)));
t++;
printf (“%u, %s”, (unsigned int) t, asctime (gmtime (&t)));
return 0;
}

Now when I run it I get this:

myname# gcc 2038.c;./2038
1000000000, Sun Sep 9 01:46:40 2001
2147483647, Tue Jan 19 03:14:07 2038
2147483648, Tue Jan 19 03:14:08 2038

Notice that it works!

Unfortunately the date command in BSD (well just any 32v derived UNIX) is set to handle two digit years. So what I’ve done is to ‘fix’ date to handle four digit years. So here is the diff:

myname# diff date.c date4year.c
94c94
< long time();

> unsigned long time();
160c160
< tv.tv_sec += (long)tz.tz_minuteswest*60;

> tv.tv_sec += (unsigned long)tz.tz_minuteswest*60;
167c167
<

> /*printf(“setting time….”);*/
228c228,229
< year = gp(L->tm_year);

> year = gp2(L->tm_year);
> /*printf(“gtime year %d\n”,year);*/
243c244,246
< year += 1900;

> /*year += 1900;
> no more 2 year dates!
> */
270a274,292
> gp2(dfault)
> {
> int c, d,e,f;
>
> if (*sp == 0)
> return (dfault);
> c = (*sp++) – ‘0’;
> d = (*sp ? (*sp++) – ‘0’ : 0);
> e = (*sp ? (*sp++) – ‘0’ : 0);
> f = (*sp ? (*sp++) – ‘0’ : 0);
> /*printf(“c %d d %d e %d f %d\n”,c,d,e,f);*/
>
> if (c < 0 || c > 9 || d < 0 || d > 9)
> return (-1);
> if (e < 0 || e > 9 || f < 0 || f > 9)
> return (-1);
> return ((f*1000)+(e*100)+(d*10)+c);
> }
>
293c315
< long waittime;

> unsigned long waittime;

Now I can set the time like this:

myname# ./date 201001021208
Sat Jan 2 12:08:00 PST 2010
myname# ./date
Sat Jan 2 12:08:01 PST 2010

And all is well.

So let’s take it to 2040!

myname# ./date 204001011433
Sun Jan 1 14:33:00 PST 2040
myname# ./date
Sun Jan 1 14:33:02 PST 2040

And there we have it!

The old date command would return Thu Nov 26 08:04:46 PST 1903, which is slightly wrong!

Naturally EVERY program that calls time/date stuff will have to be checked to be fully 2038 vetted, but you get the idea. The major offender in the date command is the idea of adding 1900 to the date, which is not needed when you specify the full year.

While this 2038 solution is simple, it still requires people to start to take action. It seems the industry is not moving yet, but it’ll certainly require source code access to make it quick & easy… But I’m sure like the Y2k issue, people will wait until 2036, and by then there’ll be good money in programs that can decompile various UNIX binaries, and change that signed long value… The ‘issue’ is the other fallout like the slight change I had to make to the 2038 test program….

Always remember to keep your source SAFE!

Oh, and happy new years!