Running a DOS-based BBS through Linux and Telnet

Since social networking has gotten OUT OF FREAKING CONTROL, a certain nostalgia has dwelled deep within me to reconnect to my roots of BBSing.

Some sites like BBS Corner claim that BBSes are even stronger now than they were in the past, in due, part to the internet and not having to dial long distance, and because most of us are broadband and can get speedier connections to telnet boards or it’s much more feasible to operate multinode setups on newer hardware.

What I will discuss, is a very simplistic way to get your BBS online (in a headless fashion) without an over-engineered solution such as VmodemD or using predefined DosBox instances.  If you have a router, you’ll have to learn how to port forward on your own.  This is outside the scope of this blog.  Although there is nothing WRONG with using VmodemD or the online example of how to get a multinode Renegade BBS setup over telnet in linux I find them to be over-engineered solutions.  I have tested this with Renegade BBS and Oblivion/2 BBS, but I suspect it will work with just about any DOS (non win32 dos) BBS software.

There’s one caveat: perhaps it’s just SyncTerm‘s implementation of Z-modem or Y-modem-g (SEXYZ), but I still haven’t gotten a reliable file transfer to work right, yet.  But who really cares?  Zmodem is basically a way of transferring files without TCP/IP’s error-correction state, which tends to mimic TCP/IP (which you don’t need since you’re using TCP/IP, right? This will just slow things down) and Y-modem-g, well, I still haven’t really find a free solution for that.  Bah, I digress, here’s how it goes (online door games will work this method, too, such as LORD, and Pimpwars, etc…) nothing:

I’ll assume you’re using Ubuntu linux, but it doesn’t really matter.  Grab yourself a copy of dosemu 1.4.0 and install it (install the latest “freedos”, as well).  Make sure you can run dosemu before you proceed any further.  Also, I will not be covering howto do that, since you could just RTFM for dosemu.

I have been informed that directly copying and pasting this stuff may not work, mainly in the way double quotes are translated during the copy and paste process, or how wordpress decides to render them.

1) I find it easy to have two copies of dosemu’s config: 1 for local use (installing things, configuring, maintenance, etc), and a mirror of this same one, with 1 minor detail changed – scroll down all the way to Serial Port Settings and change this line:
$_com1 = "virtual"

Save and exit.  This file will be used for the BBS/telnet script you will write in the next few steps.
2) load up dosemu with your first config file:

dosemu -n -f /etc/dosemu/dosemu.conf-vanilla (or something similar)

Install your environment as your normally would, like pkzip/pkunzip, fossil driver (bnu/X00 – I prefer BNU), share.exe, exitemu, etc…

VERY IMPORTANT – Make sure the last line of your autoexec.bat is: unix -e

config.sys should be similar to this:

rem config.sys for DOSEMU + FreeDOS
rem note that the initial "D:" is set to "Z:" at the end
SWITCHES=/F
DOS=UMB,HIGH
dosdata=umb
lastdrive=Z
files=40
stacks=0
buffers=10
devicehigh=c:\dosemu\ems.sys
shellhigh=c:\command.com /e:1024 /p

At this point, you should be able to type “exitemu” and it should exit your dosemu instance.  If it does not, you did not properly install freedos or the other required dosemu utilities.  This is a required utility.
3) Now you need to make a batch file to load your BBS with:
example C:\BBS.BAT

@echo off
break off
break=off
c:
cd\WHEREVER_YOU_PUT_SHARE.EXE
SHARE.EXE
cd\WHEREVER_YOU_PUT_BNU.COM
bnu.com /L0:57600,8N1 /F
SET DSZLOG=C:\OBV2\DSZLOG.%1
cd\OBV2
obv.exe -b 57600 -A -N %1
exitemu

Save that file and exit.  You will have to configure your BBS software for each node to use COM1 before attempting anything further.  Yes, I know it sounds ludicrous, but it works, trust me.
4) Install telnetd/in.telnetd with xinetd
5) Make sure your /etc/services line with port 23 tcp / telnet is uncommented
6) Make your /etc/xinetd.d/telnet match this:


# default: off
# description: An xinetd internal service which echo's characters back to
# clients.
# This is the tcp version.
service telnet
{
disable= no
port= 23
socket_type= stream
protocol= tcp
user= some valid user to run as
wait= no
server= /usr/sbin/in.telnetd
server_args= -n -h -L /usr/sbin/bbs
}

7)  Now, create /usr/sbin/bbs similarly to this:


#!/bin/bash
export HOME=/home/some valid user to run as/
unset DISPLAY
total=10
current_sessions=`ps aux | grep in.telnetd | grep bbs | grep -v grep | wc -l`
if [ $current_sessions -ge $total ]; then
echo "Too many connections, please try again later."
sleep 2
echo "Goodbye."
sleep 1
else
NODE=1
       for i in `seq 1 $total` ; do
                if [ -e "/tmp/bbsnode$i" ] ;
                then
                        DUMMY=1
                else
                        let NODE=$i
                        touch /tmp/bbsnode$i
                        break
                fi
        done
echo "Loading MYBBS [node $NODE/$total]..."
sleep 2
/usr/local/bin/dosemu.bin -n -f /etc/dosemu/dosemu.conf-BBS -E "C:\BBS.bat $NODE"
echo "Goodbye."
        rm -rf /tmp/bbsnode$NODE
sleep 1
fi

8) Give that script execute permissions: sudo chmod +x /usr/sbin/bbs
9) Restart xinetd: sudo /etc/init.d/xinetd restart
10) Try it out, take it for a test spin: telnet localhost
Debugging:
You might want to make sure you can run the BBS in LOCAL mode with your vanilla dosemu conf file first if it’s not working.
Feel free to leave comments or suggestions.
If you’ve spotted the obvious denial of service attack that can be done on this computer (with determining how many active nodes there are), I’m more than welcome to hear any ideas on how to fix it, but so far it seems to work pretty well!
Ciao!

Quick threaded app with “Go”

Wow, seriously, hats off to Google on this one…  This should make threaded apps programming easier in the future for me.  They have created a language called “Go”, which in my opinion, looks like if Python and C++ had a child out of wedlock.  I don’t think I’ll be able to stop gushing about Go for a while.  I believe I have found my new prototyping language, however, I don’t see a reason why this couldn’t run production code…

Check out this snippet of Go code:

file: threads.go

package main

 

import "fmt"
import "syscall"

 

const THREADS = 2;

 

func threadtest(a int) {
fmt.Printf("Thread %d started.\n", a);
syscall.Sleep(10);
for true {
for i := 0; i < 10; i++ {
fmt.Printf("Thread %d -> %d \n", a, i);
}
}
}

 

 

func waitforever(debug int) {
for true {
if debug == 1 {
fmt.Printf(".");
} else {
fmt.Printf("");
}
}
}

 

 

func main() {
fmt.Printf("testing threads:\n");
for i := 0; i < THREADS; i++ {
go threadtest(i);
}
waitforever(0);
}

Output:

 

$ ./threads
testing threads:
Thread 0 started.
Thread 1 started.
Thread 0 -> 0 
Thread 1 -> 0 
Thread 0 -> 1 
Thread 1 -> 1 
Thread 0 -> 2 
Thread 1 -> 2 
Thread 0 -> 3 
Thread 1 -> 3 
Thread 0 -> 4 
Thread 1 -> 4 
Thread 0 -> 5 
Thread 1 -> 5 
Thread 0 -> 6 
Thread 1 -> 6 
Thread 0 -> 7 
Thread 1 -> 7 
Thread 0 -> 8 
Thread 1 -> 8 
Thread 0 -> 9 
Thread 1 -> 9 

$ ./threads

testing threads:

Thread 0 started.

Thread 1 started.

Thread 0 -> 0 

Thread 1 -> 0 

Thread 0 -> 1 

Thread 1 -> 1 

Thread 0 -> 2 

Thread 1 -> 2 

Thread 0 -> 3 

Thread 1 -> 3 

Thread 0 -> 4 

Thread 1 -> 4 

Thread 0 -> 5 

Thread 1 -> 5 

Thread 0 -> 6 

Thread 1 -> 6 

Thread 0 -> 7 

Thread 1 -> 7 

Thread 0 -> 8 

Thread 1 -> 8 

Thread 0 -> 9 

Thread 1 -> 9 

True (easy) concurrency!  The magic happens on “go threadtest(…)”, this is called a “goroutine.”  I’ll try to write some more Go-code later… cheers!

Lucene and PHP

Today  I realized the strengths and speed of Lucene, but I also realized something else: Zend_Search_Lucene is painfully slow compared to Java’s implementation of it, and it requires a ton of memory to perform Lucene searches.  The ease of development, in this case, is not worth the hassle (and the memory leaks).

run time

The great alternative, albeit a pain in the ass to implement (due to debugging and not playing friendly with IDEs), is the PHP/Java Bridge method of doing a Lucene search.  I was able to perform a sample search against a Lucene index with Zend’s implemention in an average of 0.30 milliseconds (varying by 0.1 milliseconds) with a required 50mb of memory allotted to the PHP script on an index containing 29,000 documents.  I was able to swap out the Zend-specific objects and replace them with PHP/Java Bridge SOAP/Java methods.  I really didn’t think I had much to gain by doing this other than cutting down on my memory usage.  I thought just by the pure fact it had to make a socket connection and then make calls to the java Lucene jar file, I was going to lose time… I was totally wrong on this one.  To run the same query against the vanilla tomcat server running in local mode through the PHP/Java Bridge took a consistent 0.01 milliseconds with almost no variance.

server load

How do they compare side by side with a load test?  This one shocked me too – the Zend_Lucene_Search implementation would theoretically max out a 4gb server memory with 80 concurrent connections.  By today’s standard, a load of 80 concurrent users shouldn’t max out the server’s memory with an index size of 29,000 documents (with no extraneous binary storage) consuming ~15mb on the disk.  That is totally unacceptable.  The PHP/Java Bridge implementation survived well over a load of 1000 concurrent connections without maxing out the memory or the processor.  The only problem I ran into was “too many open files” which a “ulimit” modification (with a tomcat restart) solved.   However, I do not expect there to be thousands of connections to just this one server opened at the same time – I just wanted to see how much it could handle … and it turned out to be quite a lot!

overall

Zend_Search_Lucene PHP/Java Bridge with Lucene
Query Execution time 0.30 ms 0.01 ms
Memory usage High Low
Ease of Implementation Easy Moderate (difficult to debug)
Recommended Use Internal Apps/Speed of development Production Apps/Speed-critical applications