USB and console-mode support

I need to write a terminal program. I already have a program written in Python (not by me) but it would need significant upgrading and I don't want to learn enough Python to do this (Python is really not my kind of language).
I was planning on using GCC, because I know C from the old days (Turbo C under MS-DOS), but I'm not enthusiastic about getting into C again because it is an ugly old language. So, I'm considering Racket Scheme. I need to have support for USB though. That is the most important. I also need to have support for console-mode programming, comparable to or equivalent to NCurses (I don't want to get involved in GUI because that is a lot to learn, and it is an overkill for a terminal program).

My terminal program is intended to be the IDE for my Forth compiler for a micro-controller.

thanks for any info --- Hugh

1 Like

I should mention that I'm using Windows-11.
I would like to eventually have my terminal working under Linux too though.

Is Python using a library for USB, or does it calls some Windows API?

1 Like

Some people build sophisticated console interfaces using lux: brilliant interactive programs and its integration with raart: Racket ASCII Art and Interfaces.

I believe @dominik.pantucek has also been developing a tui-easy that might provide higher-level support for console interfaces.

But also, Racket's GUI libraries are mature and entirely cross-platform, and the gui-easy: Declarative GUIs provides a path into using it that lets you avoid having to learn Racket's object-oriented programming library.

Python uses PYSERIAL (don't make the same mistake that I made I try to use SERIAL because that has to do with serialization, not USB). Most, if not all, languages that support USB do so with a wrapper around LIBUSB that is written in C.

It seems as though Racket doesn't support USB, so I'm delving into RUST and RHAI. I would prefer Racket though because it is a lot less complicated than Rust, and it is its own scripting language so I wouldn't need anything like Rhai that goes on top of Rust. The advantage of Rhai though, is that it is supposed to be sandboxed and hence safe from malicious code that some user might write. I want to make sure that my terminal program doesn't get hijacked as the platform for malware. My program is for educational use, so it is likely to be distributed among high-school students who aren't necessarily going to look closely at any scripts provided before running them.

I'm surprised that nobody has ever provided support for USB in Racket, considering that USB is very common for interfacing with the outside world (a microcontroller, in my case). Is Racket only used for games and educational coding?

Does Racket have any protection against being hijacked as the platform for malware? I'm thinking of somebody distributing a program that does something innocuous, such as play a game or interface with a microcontroller, but is actually malware.

Hello,

I can confirm I am deep into that endeavour. However it is a long way and I have only recently finished the low-level prerequisites to support that. The tui-term[1] package provides an (in my opinion nice) abstraction of the terminal input for all three major platforms and the tui-ubuf[2] is somewhat akin to a curses like library for double-buffered output with efficient delta calculation. I have already presented some proof of concept code/video on Discord, but an optimistic estimate is that I will publish something viable before the end of the year. Also bear in mind that it requires Racket 8.17 to fully support Windows terminal which I managed to implement and test just for this release. Which is a bit ironic as I am not a Windows user since 1999. Also more extensive examples are missing, but they will definitely hit the documentation way before the actual TUI library is ready.

Well, USB is pretty generic and while there are ways to do it you typically interact with a specific type of device through it. I had to deal with it in the past using the USB HID and the CDC ACM protocols under Linux. Back in the day we used the racket-serial package[3] and it worked like a charm for a ttyACM device. For HID we just used FFI[4] and tapped into libhidapi-libusb[5]. If you want to see a funny video of a robot controlled via USB performing quality control of another device connected via USB, we have published that as well[6]. Although it looks a bit silly in retrospect :wink: Yes, all that you see on the screen is controlled solely by a Racket program - no helpers written in any other language.

As someone who works for more than 25 years in the security/cryptography field I find this request rather strange. How could a language protect against that without limiting what is possible? That is a job for other operating system access control mechanisms. All the attempts for achieving this failed miserably (think Java or .Net "sandboxes").

Cheers,
Dominik

[1] TUI Terminal Support
[2] TUI Universal Buffers
[3] serial
[4] The Racket Foreign Interface
[5] GitHub - libusb/hidapi: A Simple cross-platform library for communicating with HID devices
[6] Cryptoucan™ Manufacturing: Keypad Testing

1 Like

I remember when they fixed a Linux kernel bug in USB support
and after that my scanner never worked again.

-- hendrik

1 Like

Well, yeah! It would be a scripting language that significantly limits what is possible.

I gave some thought to the subject of security in regard to a Safety Forth, and this would be applicable to a Safety Scheme too. It would be 16-bit with three 64KB segments, for machine-code, threaded-code and data. The user has no access to the machine-code segment, as I alone write primitives. All code would compile into the threaded-code segment (in Forth this would be via the COMPILE, word). In regard to data-access, the user has only limited access (in Forth this would be via @ ! etc.). I might have another 64KB segment with limited access (in Forth, ES@ ES! etc.).

Something like this should be adequate for allowing the user to write scripts that communicate with the 16-bit micro-controller. These scripts send and receive messages via the USB. The scripts (and the corresponding code on the micro-controller) are used for setting parameters for the machine and obtaining progress reports on what the machine is doing out there.

Does your 25 years in the security field involve any experience with Rhai? It is supposed to be sandboxed. Has it also failed miserably? Rhai is one of the slowest scripting languages available for Rust. I assume that at least part of the reason is that it limits memory access to the sandbox. If you are going to have 32-bit pointers, then you have to boundary-check every memory access as the program runs (with 16-bit pointers, they limit themselves to their own 64KB sandbox so there is no speed penalty).

I remember that Java was designed to be secure, but it failed, although I don't know why.

You can use libusb from Racket via The Racket Foreign Interface. If you even considered using C for this project, you should have no trouble writing the FFI code.

Your observation that Racket "is its own scripting language" gets to the tension that @dominik.pantucek is trying to put out. In the case of Rust and Rhai (from my quick reading), Rhai's APIs allow the Rust host application to restrict the capabilities of Rhai scripts, but the capabilities of Rust are not restricted. The sandboxing provides some useful protection, but there's always the possibility of bugs in Rhai's sandbox implementation, and likewise the Rust application could give the Rhai scripts more capability than they realize.

With Racket, there are still two distinct contexts, even though the languages on both sides may both be called Racket. To write a host application, you may need to do things like load and use an arbitrary C library (e.g. libusb), which is inherently unsafe. Racket lets you write unsafe code when you need to. (As a point of comparison, the Rhai documentation says its implementation does use some unsafe Rust.) But Racket also helps you write a host program that evaluates user programs with limited capabilities.

In fact, because of its history, Racket has particularly rich support for sandboxed evaluation. In a sense, Racket was created because they needed a language in which to write DrRacket, and DrRacket needs to protect itself from buggy user programs that never terminate or allocate too much memory. There's also a server for checking (and thus running) homework submissions that uses these facilities.

For what you describe, the racket/sandbox library sounds very suitable. A particularly effective way to use it is to restrict user programs to a module language you create, so that they don't even have access to everything in racket/base. You may also want to read about some of the language constructs that make the library possible, including structure inspectors, code inspectors (and protect-out), security guards, and custodians.

I'll also drop a pointer to Spritely and their work on object capabilities as a really principled approach to untrusted code. But again, I think racket/sandbox is a good solution for what you describe.

@LiberalArtist and @dominik.pantucek are correct in that a general-purpose language such as Racket cannot provide security per se.

@HughAguilar is also on the right path with his idea that a scripting language can address (not solve) the problem rigorously. But the word "scripting" must be understood as "severely restricted expressive power" in this context.

The Shill scripting language is an example of such a scripting language. While implemented in and based on Racket, it uses a capability model to restrict Racket's power (and explicit re-linking to certain OS functionalities, found on some Linuxes). The authors have used it in contexts that require security (and safety), e.g., running students' homework solutions when those programs can supposedly access anything on the host machine. @HughAguilar might want to look at it. in case a similar scripting language could address his problems.

1 Like

I'll look into this. I have to get USB support first, before anything else happens, because USB is the crux of a terminal program.

I'm still surprised that nobody has written a wrapper for LIBUSB already. Am I really the first person to want to access USB ports from Racket? This would be my first Racket program, and I am already required to adventure into unexplored territory.

I'm hesitant to delve into Rust because it is a big language (touted as being the "C++ killer"), which is an overkill for a terminal program.

I'm also hesitant to delve into Rhai because it is a big language with dynamic typing and all that 21st century hoohaa. It might have bugs, or at least features that I don't know about, that make it unsafe. I would feel safer with a small uncomplicated scripting language that I write myself.

When I was thinking about writing Safety Forth in the past, I was assuming that I would write it in 64-bit x86 assembly-language, but use 16-bit indices for all of the memory-access words available to the user to restrict the user to 64KB segments.

Does Racket have an x86 assembler available?
I suppose I could write my scripting language in FASM and then use your foreign-language interface to access it from Racket.

That foreign-language interface seems to be a big part of entry-level Racket programming.

My understanding is that Java was also developed for use in schools to run student programs safely.

In the bad old days, Pascal was the primary language used in schools, but it has no safety whatsoever. This was especially bad because, in those days, schools typically had a central computer (often a VAX) that was not only used for running student programs, but also was used by administrators for working with sensitive data such as the students' grade records. Students might want to gain access to those records and modify them!

On Apr 30, 2025, at 11:05 PM, Hugh Aguilar via Racket Discourse notifications@racket.discoursemail.com writes that his "understanding is that Java was also developed for use in schools to run student programs safely.”

Absolutely not. Java (successo of Oak) was built as a language for “running things on the web,” as a memory-safe and somewhat type-safe successor to C++. The syntax was adjusted to make people think that the two languages were similar, with Java somewhat more convenient. So people bought the cover of the book w/o understanding that the content was quite different.

1 Like

I'll look into this. I have to get USB support first, before anything else happens, because USB is the crux of a terminal program.

If you really need something that low-level. Didn't Unix already have a
terminal interface long before nyoe invented USB? Didn't that get properly
virtualised when X Windows came along? Isn't it still present in Linux?

I'm still surprised that nobody has written a wrapper for LIBUSB already. Am I really the first person to want to access USB ports from Racket? This would be my first Racket program, and I am already required to adventure into unexplored territory.

Maybe because they didn't really need it.

Does Racket have an x86 assembler available?

I don't know of an assembler being built into the Racket system. But if Racket
compies to machine code, I suppose there must be some similar mechanism in there
someplace.

I suppose I could write my scripting language in FASM and then use your foreign-language interface to access it from Racket.

That foreign-language interface seems to be a big part of entry-level Racket programming.

It seems to be based on the function calling conventions used by C.
It's certainly possible to write compatible code in assembler. But why would you when
C is the lingua franca of Linux?

My understanding is that Java was also developed for use in schools to run student programs safely.

Basic was originally developed for this purpose.
I believe java's first serious use was to program set-top boxes to connect
cable TV networks to television sets. Evey manufacturer's box was different,
so Java's "run everywhere" slogan hit the spot.

In the bad old days, Pascal was the primary language used in schools,
but it has no safety whatsoever.

It's possible to make a secure Pascal compiler, but nobody does. You'd have
to implement garbage collection (as a rather heavy check on the use of new
and dispose) and prevent the misuse of variant records to evade type checking.

This was especially bad because, in those days, schools typically
had a central computer (often a VAX) that was not only used for
running student programs, but also was used by administrators for
working with sensitive data such as the students' grade records.
Students might want to gain access to those records and modify them!

That wasn't a problem with the language as much as with the operating
systems the central computer was run with. VAX/VMS was actually quite
devent in this respect. The CDC Cyber series of machines was not.

-- hendrik

Does Racket have an x86 assembler available?

The place to look is:
https://pkgs.racket-lang.org/

This looks the most promising:
x64 Assembler
But see also:
3 a86: a Little Assembly Language

This would hopefully work well for writing the primitives of the Safety Forth scripting language. I still need to learn how to call Racket functions from assembly-language.

I would have to go with indirect-threading. I considered subroutine-threading but that won't work because the return-stack pointer is 64-bit, so an attacker could overflow or underflow the stack to access memory outside of the 64KB segment. With indirect threading I can use 16-bit pointers for both data-stack and return-stack. Threaded code is okay because I'm not focused on speed. The primitives would be written in assembly-language, so that helps boost the speed somewhat.

I'm using Windows-11, not Linux.
The microcontroller has these functions: INIT-SERIAL, WRITE-SERIAL, READ-SERIAL and CHECK-SERIAL (to determine if there is data available for READ-SERIAL).
The host computer should be the same except that I also have to find out how many USB ports there are and allow the user to choose one of them, as only one of them is connected to the microcontroller and there may be other doodads on other USB ports. This doesn't seem very low-level to me. I'm not writing a device-driver that accesses the hardware. I'm expecting the data to be buffered internally, so I don't have to deal with that.

Looking at this comment and based on your previous comment that the existing Python program uses PYSERIAL, it may be that your microcontroller uses an emulated serial port (COM port) over USB. This would definitely be the case for an Arduino, for example.

If this is the case for your microcontroller, you don't need to access USB functionality directly using libusb. Instead, your program needs to open and communicate using a serial port.

There's a racket package libserialport which allows accessing serial ports using the DLL by the same name (the DLL needs to be installed separately). Perhaps it might be worth looking into using this package?

Alex.

2 Likes

I'm a bit confused about why you want to implement Safety Forth in assembly language. At first I thought it was to run directly on your microcontroller, but I'd be surprised if your microcontroller really uses x86_64. If Safety Forth is what you mean to offer as the scripting language for your IDE, it would be more usual to implement it as a language that expands to Racket (or another Racket-based language), which gives you memory safety out of the box (unless you opt out by explicitly using unsafe libraries). Or maybe Safety Forth is for another purpose?

The microcontroller uses an FT245 chip that is communicating by USB. The FT245 buffers the data internally, so my microcontroller doesn't need to buffer the data or include an ISR as was done for RS-232 in the past. I did this in the past with RS-232 and I had 19,200 baud which is somewhat sluggish. The FT245 transfers data hella fast!

Yes, this is just a scripting language for the host computer. I'm really getting way ahead of myself in thinking about the scripting language before the IDE is written.

The IDE doesn't actually need a scripting language. The scripting language is for a terminal program similar to the IDE that is used by the end-user for setting parameters on the microcontroller or obtaining information about what the microcontroller is doing (such as in a factory machine). This needs a scripting language because the end-user has to customize this for his application. The IDE doesn't need to be customized for any application --- it is just used for developing the microcontroller program --- the end-user never uses the IDE, although the programmer that he hires to develop his program will use the IDE during development (and that programmer writes the code on the microcontroller and in the host scripting language that the end-user will use).