0

I am running embedded Linux and my command line has console=/dev/ttyO0,....
I have terminal program connected to this UART and can see EVERYTHING produced by the system. Now, when running in the field, I want to store all this output in the file (I have the media connected). But all my efforts using syslogd, klogd, logger and Co. do not give me the desired result - some messages are stored, but some are not - usually the most improtant.
For example, if I run:

#myapplication.sh | logger        

"echo" commands I see in syslog, prints from the application too, some drivers messages too. But if the application crashes with segmentation fault this info is not logged. But this is most valuable!
So, the best and most desired is to redirect ttyO0 to a file, but how?
I found such a recommendation:

(stty raw; cat > received.txt) < /dev/ttyO0

but this doesn't work: I can type a command, f.e. ifconfig, but reply does not appear neither in the file nor on console. Even the file is not created...:-(
If it is possible to have both - file and serial console - it will be brilliant. But file has higher priority.

Many thanks for a help!

UPDATE: I found the answer - see the last one.

4
  • Please have a look at unix.stackexchange.com/a/200642/254422
    – binarysta
    Aug 14, 2020 at 21:53
  • Thank you, benarista, but this doesn't work. At least kernel messages from drivers are not stored. Also running "script" from script is problematic - it exits arbitrary or produces empty files. But major problem - kernel messages are not stored.
    – leonp
    Aug 14, 2020 at 23:16
  • Dear Benarista, how can I start "script" command from within a bash script? It does nothing and hangs when I do this.
    – leonp
    Aug 15, 2020 at 11:48
  • Starting "script -a -f" in background makes my script to run, but the log contains only i/o from console, not from the script itself.
    – leonp
    Aug 15, 2020 at 12:07

4 Answers 4

3

You seem to have missed one fairly basic fact. Every pipeable program relies on the fact that it gets three standard file descriptors in a pre-opened state from whatever program invokes it. In most cases involving pipes, the invoking program will be the command shell.

These three file descriptors are:

  • file descriptor #0: standard input stream, or stdin for short.
  • file descriptor #1: standard output stream, or stdout for short.
  • file descriptor #2: standard error output stream, or stderr for short.

When a program is not being piped or redirected to read/write a file, then all these three file descriptors will usually point to a TTY device the current login session is running on. If a program is deliberately started as a daemon or a background/non-interactive process, all of these might be directed to file(s) or /dev/null, or possibly into some sort of logging facility.

Error messages are normally output into the stderr stream, precisely so that they won't get mixed in with the possibly-piped data output. But in cases like your, when you wish to capture all the output, you need to explicitly tell the system to grab both streams.

If you want to pass all the output from your myapplication.sh into a pipe, you might have to do something like this:

myapplication.sh 2>&1 | logger

The 2>&1says "redirect file descriptor #2 to the same place #1 is going", and then you can pipe both of them at once.

You can test it yourself with a little script like this:

#!/bin/sh
echo "this is stdout"
echo "this is stderr" >&2

The >&2 means "take the standard output of this command and send it into the standard error stream instead". This is probably the easiest way to generate stderr output in a script.

Now, if you run this script like ./test.sh | od -t x1z to get its output in hex-dumped form, you'll get this:

$ ./test.sh | od -t x1z
this is stderr
0000000 74 68 69 73 20 69 73 20 73 74 64 6f 75 74 0a     >this is stdout.<
0000017

So only the stdout output got processed by the odcommand.

If you add the 2>&1 to merge both streams, you'll get this instead:

$ ./test.sh 2>&1 | od -t x1z
0000000 74 68 69 73 20 69 73 20 73 74 64 6f 75 74 0a 74  >this is stdout.t<
0000020 68 69 73 20 69 73 20 73 74 64 65 72 72 0a        >his is stderr.<
0000036

Now both outputs were sent into the pipeline, which is what you want in your case with | logger.


Your second command line is capturing whatever your embedded device is receiving from the terminal program, not what the embedded device is sending to the terminal:

(stty raw; cat > received.txt) < /dev/ttyO0

You said you're working on an embedded system that uses the kernel parameter console=/dev/ttyO0, so essentially /dev/console will be a serial port, not a graphical display (which may not even exist in an embedded device).

If you are running that command on the embedded device, the commands inside the parentheses will run with their standard input stream (file descriptor #0, or stdin for short) assigned to /dev/ttyO0. But if you are entering that command over the serial line connected to ttyO0, that's what it's probably going to be by default, so this input redirection may just be re-enforcing the situation as it is.

The /dev/ttyO0 device is the direct representation of the UART that is connected to your terminal program on the other computer. When you read from it, you get only what you are typing into the terminal program: there is no way to read back any output that was previously sent into it. And anything written into it goes directly to the terminal program at the end of the serial cable.

The Unix command line on a serial port normally works on a remote echo principle: in order to see what you are writing on the terminal program, the TTY driver on the embedded system must send a copy of every character you type right back to the terminal program. This behavior is not symmetric: the terminal program will not be also sending back a copy of anything it receives over the serial port connection.

The TTY driver is a complicated thing, because Unix heritage requires it to be capable of doing a lot of things - the remote echo feature is just one of them.

When you use stty raw, you're effectively switching off all the normally-helpful features of the TTY driver, including the remote echo feature. So, from that point onwards, unless something explicitly reads from /dev/ttyS0 and writes it immediately right back into it, you won't see what you are writing on the terminal program.

Once the stty raw command has exited, any input from the terminal program will go into the cat command, which would just pass it into its standard output as-is when invoked with no parameters. But its standard output has now been redirected into a file, received.txt. So what happens is that only what you're typing on the terminal program gets written into the file until the cat command ends - and it only ends when it gets the end-of-file character (typically Control+D).

(Because of whatever buffering might be done by the shell doing the redirection, there might be no output into the received.txt file unless you end your typing with the end-of-file character, and/or type more than one line of text.)

So, other than switching off the TTY driver features, the (stty raw; cat > received.txt) < /dev/ttyO0 on the embedded system does nothing at all to capture any output that is being written into /dev/ttyO0. The only use for this command would be when you're troubleshooting the serial port connection: if you are suspicious that your terminal program is somehow mangling the characters you're sending out, this is the way to capture whatever the terminal program is sending to the embedded device in its most raw form.


If you want to record absolutely everything that happens over the UART connection, including e.g. kernel panic messages, then the best way to do it would probably be to tell your terminal program on the other computer to log all the traffic. Many terminal programs have this feature built-in.

This is because if something is going wrong with the kernel, it might not be actually possible to write anything into a disk file any more. It's much simpler to just send the error message text out of an UART and trust that whoever is receiving it will catch it.

8
  • Dear telcoM, thank you for the detailed answer. May be I missed your point, but you didn't recommend me what to do and didn't say that ths is impossible...:-) What I want is simple: all I see on the PC connected to the UART to be stored in a file on the embedded device. This will allow me to analyse possible fails. I don't type a thing in the 'field" - nobody is there. Just kernels and program's outputs are sent to console by kernel and programs.
    – leonp
    Aug 17, 2020 at 13:51
  • I understand that this is TTY output going to the serial line, but I don't believe that there is no way to copy it somehow to somewhere. Redirecting output(s) from specific program(s)/daemons/etc is obvious, but besides it is strange to run on each and redirect, this is also not sufficient, as kernel messages (from drivers, for example) are missing.
    – leonp
    Aug 17, 2020 at 13:51
  • I am trying to play with /dev/vcs but no success yet.
    – leonp
    Aug 17, 2020 at 14:09
  • The kernel messages may come from an entirely different output routine: instead of the full TTY driver, the panic routine just pushes the error message directly out of the physical UART. When the system is panicing, you might no longer have the ability to write into files: perhaps the panic was because the disk or filesystem driver has detected a fatal error? To capture truly everything, including kernel panic messages, you really need to look at external solutions, of which the easiest is to use the logging capability of the terminal program at the other end of the serial link.
    – telcoM
    Aug 17, 2020 at 14:14
  • /dev/vcs gives you only the way to peek into virtual consoles, which are a feature of the GPU-based console driver only: on serial consoles, you don't have that unless you run something like screen or tmux for similar functionality. To get everything from userspace, ttysnoop might be a solution; but the kernel messages to the serial console can bypass it.
    – telcoM
    Aug 17, 2020 at 14:17
1

Try using picocom instead of console=/dev/ttyS0. There are other tools but picocom sounds the easiest.

If you use one of these tools you can use a pipe and tee on the output.

picocom /dev/ttyS0 -b 115200 -l | tee my.log

https://askubuntu.com/q/1010232

2
  • Please use syntax for formatting code
    – mattia.b89
    Aug 16, 2020 at 14:07
  • i am afraid this is not work as picocom will work in the opposite direction - system output sent by Linux to console is an output for picocom, IMHO.
    – leonp
    Aug 17, 2020 at 13:55
0

OK, I think I found the answer. I am very very surprised that such a simple and required IMHO thing is solved in so "irregular" way, but this is the life...:-)
Anyway, thanks to telcoM who gave me the references which lead to references and more references and finally allowed to find the ttyrpl package which does exactly what I wanted.
The package maintaining stopped some 7-10 years ago and it took me some time to adopt it to my Linux version and required libraries, but it work!
It does exactly what I wanted - collects the outputs of ttyS0/ttyO0 and stores it to a file. And has a lot of fancy options although I don't need them...:-)

0

Try https://tio.github.io - it supports logging to file. For example:

tio /dev/ttyS0 --log --log-file my-log.txt

Or

tio /dev/ttyS0 --log

Which logs serial output to file with auto-generated filename such as tio_ttyS0_2022-09-14T08:56:09.log

1
  • Thanks, Martin, but you missed the main point - it is an embedded system and I need to store serial output for analysis in the field, when there is no PC connected.
    – leonp
    Sep 15, 2022 at 13:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .