Let SOcket CAT Be Thy Glue Over Serial
Introduction
I previously wrote how to configure minicom to connect to a device over serial UART. In that post I mentioned that minicom might not be the best tool for the job. Consider that minicom
is adding a lot of unnecessary complexity. If you think about it, we are taking a command line utility intended for communicating with modems, stripping all that functionality away so that we are left with nothing more than a terminal emulator that knows how to connect to a foreign TTY
. Yet, we are probably running minicom
from a perfectly capable terminal emulator such as xterm
. All we need is a way of connecting our terminal emulator to the target serial port in raw mode. As long as the device side is running a login on the receiving end, we can simply let our terminal emulator do what it was designed for.
Introducing SOcket CAT
With SOcket CAT (see socat(1)) we can connect a serial port to a terminal emulator in raw mode.
socat -,rawer /dev/ttyUSB0,b115200,rawer
SOcket Cat relays two bidirectional byte streams given their address specifications. The order of the address specifications is irrelevant. The following command is equivalent:
socat /dev/ttyUSB0,b115200,rawer -,rawer
Consider the address specification which targets the standard I/O
of the client side emulator:
-,rawer
The hyphen at the beginning is actually an alias for the address type STDIO
. Some address types take parameters delimited by a :
. The comma separated list that follows are the address options. Address options are defined in option groups. STDIO
accepts options from the FD, TERMIOS, REG, SOCKET option groups. The rawer
address option is defined in the TERMIOS option group. It initializes the line discipline in raw mode and implicitly turns off echo.
Now consider the second address specification.
/dev/ttyUSB0,b115200,rawer
There is a rule that if a forward slash is found before the first colon or comma of an address specification, then the address type implicitly defaults to GOPEN
(Generic file Open) and accepts the (FD, REG, SOCKET, NAMED and OPEN) option groups. In other words, /dev/ttyUSB0
is an address parameter to GOPEN
. After that the address option list begins. The first address option, b115200/
, sets the baud rate to 115200. The second address option, which we are already familiar with, causes the /dev/ttyUSB0/
to be initialized in raw mode and implicitly turns off echo.
You might be wondering how rawer is a valid option since it is defined in the TERMIOS option group and TERMIOS was not listed as one of the option groups available to GOPEN
. In fact, the baud rate option is also from TERMIOS. SOcket CAT makes an exception for addresses that work on a TTY
by making the TERMIOS option group available.
If you prefer to avoid aliases and implicit address types, then the following command is equivalent.
socat STDIO,rawer GOPEN:/dev/ttyUSB0,b115200,rawer
Interrupting SOcket CAT
At some point you will want to leave your SOcket CAT relayed serial connection. You might try ^C
which is the default way to interrupt the foreground job. However, the byte will not be interpreted by terminal emulation on the client side, but instead will be passed through interrupting the foreground process on the device side. There are many ways to achieve the desired behaviour. The SOcket CAT man page (see socat(1)) recommends using the escape address option from the APPLICATION option group. The escape
option accepts a keysym hex code identifying a key that, when pressed, will send an EOF
to the input stream thus ending the relay.
socat -,rawer,escape=0x0f /dev/ttyUSB0,b115200,rawer
The key identified by 0x0f
is C-o
. To get a complete list of keysyms known by loadkeys(1) run dumpkeys -l
. If you ever forget to include the escape option, then you can still interrupt socket
from another terminal by sending SIGINT
(see signal(7)) via kill(1) given the pid (Process ID). You can get the pid using pidof(1).
With socat
running in one terminal, open a second terminal and run the following command to signal socat
with SIGINT
.
kill -2 `pidof socat`
The nmtui test
I wanted to confirm that using socat
as the glue between serial and STDIO
byte streams would in fact allow me to run ncurses applications without garbling the display. To test this out, I connected my PinePhone to /dev/ttyUSB0
, opened an xterm
session and then ran the aforementioned socat
command.
Since my PinePhone was already booted at the time of connection, there was no login prompt waiting to greet me. I simply typed my username into the blank space and hit <RET>
. At that point the password prompt was displayed as bytes were received by SOcket CAT and relayed through standard output of my xterm
session. After I successfully signed into my shell account, I ran nmtui
and it displayed on my screen without any undesirable artifacts.
My PinePhone is running postmarketOS with ash
as the login shell. I configured the serial port of the PinePhone to use linux
terminfo so that I can run colour displays.
Logging
In the comments section, N mentioned that Screen is capable of logging. Screen is a multiplexer that can be used to connect to login sessions over serial. I wanted to find out what options there are for logging when using SOcket CAT. In socat(1) there is an OPTIONS section that describes command line options for modifying the behaviour of socat
. After reading that section, I was able to log diagnostic information to ~/dev/log/socat_debug.log
and transfer data to ~/dev/log/socat.log
. Let’s take a look at the command for doing this:
socat -v -D -d -d -d -d -lh -lf ~/dev/log/socat_debug.log -,rawer,escape=0x0f /dev/ttyUSB0,b115200,rawer 2>~/dev/log/socat.log
Options that affect transfer data logging:
-v
- writes transfer data to
stderr
using>
and<
prefixes to indicate direction -D
- Log file descriptor information
2>~/dev/log/socat.log
- Redirect
stderr
to/dev/log/socat.log
Options that affect diagnostic logging
-d
- Increase the level of diagnostic logging. When absent, only errors are logged.
-lh
- Add hostname to diagnostic messages.
-lf ~/dev/log/socat_debug.log
- Write diagnostic messages to named log file
To demonstrate, I connected to my PinePhone using the above SOcket CAT command. A login session was already running, so there was no need for me to sign in. I ran ls
as soon as the connection was established and then exited the session by sending a SIGINT
to the socat
process.
dustfinger@galactica ~ $ socat -v -d -d -d -d -lf ~/dev/log/socat_debug.log -,rawer,escape=0x0f /dev/ttyUSB0,b115200,rawer 2>~/dev/log/socat.log
ls
Pictures dialer.log dialer.log.bk
pine64-pinephone:~$ dustfinger@galactica ~ $
First, let’s look at the contents of socat.log:
The file descriptor information is written before the transfer phase begins, so it will always be at the head of the file.
FD type device inode mode links uid gid rdev size blksize blocks atime mtime ctime cloexec flags sigown sigio
0: chrdev 0,44 7 020620 1 1000 5 136,4 0 1024 0 Thu May 21 06:16:49 2020 Thu May 21 06:16:49 2020 Sat May 16 07:35:11 2020 0 x008002 0 0 /dev/pts/4 IFLAGS=00000000 OFLAGS=00000000 CFLAGS=000000b0 LFLAGS=00000000 cc[0]=^C cc[1]=^\ cc[2]=^H cc[3]=^U cc[4]=^D cc[5]=^@ cc[6]=^A cc[7]=^@ cc[8]=^Q cc[9]=^S cc[10]=^Z cc[11]=^@ cc[12]=^R cc[13]=^O cc[14]=^W cc[15]=^V cc[16]=^@ cc[17]=^@ cc[18]=^@ cc[19]=^@ cc[20]=^@ cc[21]=^@ cc[22]=^@ cc[23]=^@ cc[24]=^@ cc[25]=^@ cc[26]=^@ cc[27]=^@ cc[28]=^@ cc[29]=^@ cc[30]=^@ cc[31]=^@poll: OUT,
1: chrdev 0,44 7 020620 1 1000 5 136,4 0 1024 0 Thu May 21 06:16:49 2020 Thu May 21 06:16:49 2020 Sat May 16 07:35:11 2020 0 x008002 0 0 /dev/pts/4 IFLAGS=00000000 OFLAGS=00000000 CFLAGS=000000b0 LFLAGS=00000000 cc[0]=^C cc[1]=^\ cc[2]=^H cc[3]=^U cc[4]=^D cc[5]=^@ cc[6]=^A cc[7]=^@ cc[8]=^Q cc[9]=^S cc[10]=^Z cc[11]=^@ cc[12]=^R cc[13]=^O cc[14]=^W cc[15]=^V cc[16]=^@ cc[17]=^@ cc[18]=^@ cc[19]=^@ cc[20]=^@ cc[21]=^@ cc[22]=^@ cc[23]=^@ cc[24]=^@ cc[25]=^@ cc[26]=^@ cc[27]=^@ cc[28]=^@ cc[29]=^@ cc[30]=^@ cc[31]=^@poll: OUT,
6: chrdev 0,6 4477029 020660 1 0 14 188,0 0 4096 0 Thu May 21 06:13:44 2020 Thu May 21 06:13:44 2020 Thu May 21 04:38:53 2020 1 x008402 0 0 /dev/ttyUSB0 IFLAGS=00000000 OFLAGS=00000000 CFLAGS=00000030 LFLAGS=00000000 cc[0]=^C cc[1]=^\ cc[2]=x7F cc[3]=^U cc[4]=^D cc[5]=^@ cc[6]=^A cc[7]=^@ cc[8]=^Q cc[9]=^S cc[10]=^Z cc[11]=^@ cc[12]=^R cc[13]=^O cc[14]=^W cc[15]=^V cc[16]=^@ cc[17]=^@ cc[18]=^@ cc[19]=^@ cc[20]=^@ cc[21]=^@ cc[22]=^@ cc[23]=^@ cc[24]=^@ cc[25]=^@ cc[26]=^@ cc[27]=^@ cc[28]=^@ cc[29]=^@ cc[30]=^@ cc[31]=^@poll: OUT,
Each key stroke was logged as they were both sent and received.
> 2020/05/21 06:16:57.085563 length=1 from=0 to=0
l< 2020/05/21 06:16:57.087128 length=1 from=0 to=0
l> 2020/05/21 06:16:57.421553 length=1 from=1 to=1
s< 2020/05/21 06:16:57.422932 length=1 from=1 to=1
s> 2020/05/21 06:16:59.501568 length=1 from=2 to=2
\r< 2020/05/21 06:16:59.503115 length=2 from=2 to=3
\r
The output from ls
was transferred back to the client side.
< 2020/05/21 06:16:59.527226 length=97 from=4 to=100
.[1;34mPictures.[m .[0;0mdialer.log.[m .[0;0mdialer.log.bk.[m\r
pine64-pinephone:~$ .[6n> 2020/05/21 06:16:59.529196 length=8 from=3 to=10
.[57;21R
There was quite a bit of information logged to socat_debug.log. I will only show six lines of output below:
2020/05/21 06:16:59 galactica socat[3366] D data loop: sock1->eof=0, sock2->eof=0, closing=0, wasaction=1, total_to={0.000000}
2020/05/21 06:16:59 galactica socat[3366] D select(7, &0x41, &0x0, &0x0, NULL/0.000000)
2020/05/21 06:17:15 galactica socat[3366] N socat_signal(): handling signal 2
2020/05/21 06:17:15 galactica socat[3366] N exiting on signal 2
2020/05/21 06:17:15 galactica socat[3366] N socat_signal(): finishing signal 2
2020/05/21 06:17:15 galactica socat[3366] N exit(130)
I ended the session by sending a SIGINT
to the socat
process. You can see in the diagnostic logging when socat
receives the signal and then handles it by exiting with code 130.
I am not sure how the logging capabilities I have demonstrated here compare to what Screen is capable of. I use to use Screen years ago, but since I have embraced Emacs
as both my editor and windows manager I have very little need for a multiplexer. A serial line sniffer such as slsnif can also be used to log information from a serial connection.
Conclusion
My motivation to use minicom
to connect with my PinePhone over serial was stemed by the fact that I was using it anyway to configure the PinePhone’s modem. I thought that it would be convenient to use this one tool to connect to the PinePhone and configure the modem. However, SOcket CAT
makes it so easy to connect over serial UART that unless you have a good reason to use something else, I think you should consider letting SOcket CAT be your default glue.
Next I will be posting about setting up postmarketOS with Plasma Mobile and manually configuring oFono for my SIM.
Comments
Hi N!,
I added a Logging section to demonstrate logging capabilities using a combination of SOcket CAT command line options and bash file descriptor manipulation. I would love to hear how that compares to Screen’s logging capabilities.
Trevor Wilson
21/05/2020
Hi N!,
Thanks for commenting. Screen is a popular choice. Many people use Screen simply because it is a terminal multiplexer.
SOcket CAT provides command line options which facilitate logging, although I have not experimented with them yet. By default, SOcket CAT writes messages to stderr. The -v option tells SOcket CAT to write transferred data to stderr in addition to the target stream. The -lf option accepts a path to a file that will cause SOcket CAT to write messages to the named file instead of stderr. I suspect that combining these optional parameters will allow one to log not only SOcket CAT messages, but also the output from the interactive terminal session. I will test this out when I am not busy with work and let you know what I can come up with.
Cheers!
Trevor Wilson
20/05/2020
I’ve always found minicom cumbersome. For the past decade my goto has been GNU screen: screen /dev/ttyUSB0 115200 8N1 As an added bonus screen supports logging too.
N
19/05/2020