job control doesn't work when I try to set up getty over serial

I am trying to set up getty to log in over serial (mainly as an experiment).

With almost any configuration, the same thing happens. If my default shell is bash, I get this message after I log in:

-bash: cannot set terminal process group (15297): Inappropriate ioctl for device
-bash: no job control in this shell

and then to prove that it doesn’t work, I can’t use ctrl+C to stop programs:

$ sleep 30

and it doesn’t seem to send the signal.

These are the configurations I have tried:

I have tried both of these commands

# copied from raspberry pi:
sudo /sbin/agetty --keep-baud 115200,38400,9600 ttyUSB0 vt220
# something else I read somewhere
sudo getty -L ttyUSB0 9600 vt100
# (I know I'm mixing and matching a lot of differences but the result is the same)

I have tried both screen and picocom as a client.

I have tried using a rasberry pi as a server, and two different ubuntu laptops.

I have tried two FTDIs, two RS-485 usb adapters, and a built in RS232 on the getty side with a USB RS232 on the client side.

I have also tried changing my default shell to sh and dash. I don’t get the message, but ctrl+C still doesn’t work as expected

The funny thing is – when raspberry pi’s automatically configure /dev/ttyAMA0, and it uses exactly the getty command that I have put, job control works!

And the terminal settings are almost identical. (except for -iutf8 actually)

here are the terminal settings with the FTDI connection, and picocom running:

$ stty -a -F /dev/ttyUSB0
speed 9600 baud; rows 24; columns 80; line = 0;
intr = ^C; quit = ^; erase = ^?; kill = ^U; eof = ^D;
eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = <undef>; discard = <undef>; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr
-icrnl ixon ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0
tab0 bs0 vt0 ff0
isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase
-tostop -echoprt echoctl echoke -flusho -extproc
$ stty -a -F /dev/ttyUSB1
speed 9600 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^; erase = ^?; kill = ^U; eof = ^D;
eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V;
discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr
-icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0
cr0 tab0 bs0 vt0 ff0
-isig -icanon -iexten -echo echoe echok -echonl -noflsh -xcase
-tostop -echoprt echoctl echoke -flusho -extproc

What am I doing wrong? And why does it work with the built in configuration for the built in serial port on the raspberry pi?

Asked By: Alex028502


It’s not the commands but the environment in which they run that is the difference.

Normally getty is spawned directly from the system service manager (init) – both with systemd where it is a .service, and in the SysV world where it has an inittab entry (and not an init.d script!). This has several differences from being spawned from within another terminal:

Primarily, a process started from a terminal inherits it as its "controlling terminal", which is the most important parameter for shell job control. You can see this in ps aux or ps -ef – service processes have no ctty at first, so when getty opens the indicated terminal, that becomes its controlling terminal for job control once the shell is run.

But a getty that was started from your xterm will continue to have that xterm pty as its controlling tty despite its input/output now being routed to the serial port – and while getty itself doesn’t mind, the shell will also inherit the wrong controlling tty, and that’ll make job control impossible.

$ ps -C agetty
  PID TTY          TIME CMD
 1136 tty1     00:00:00 agetty
14022 pts/22   00:00:00 agetty
      ^-- should be ttyS0!

The controlling terminal defines /dev/tty; it defines which processes job-control signals are sent to; it defines which processes are killed (SIGHUP’d) once the terminal closes. If your shell has inherited a controlling tty that’s different from its stdin/out tty, all kinds of weird things may happen.

There are ways that a process can detach from its previous terminal, such as calling setsid() – and traditionally /etc/init.d scripts did this to ‘daemonize’ – but getty does not use them automatically because it doesn’t expect to be run this way, so it wasn’t programmed in. (There is a setsid tool that could be used to force this to happen, but you shouldn’t use it here either; you should do things the right way from start.)

You should ideally just systemctl start serial-getty@ttyUSB0 and let getty run in a clean environment. If custom options are needed, it’s better to customize that service using systemctl edit [--full] instead of running getty directly. (If systemd is not used, then edit /etc/inittab; it usually has an example included. Run telinit q to reload the inittab.)

There are many other, relatively minor differences between processes started from a shell vs through a service manager – stdin/stdout/stderr will start off as being your terminal at first (getty will close and reopen them, but not all services do); environment variables will be inherited from your ‘sudo’; the cgroup will be inherited, which might affect your resource limits; from cgroups, the systemd-logind session will be inherited, and the serial login will not be permitted to start its own); your SELinux security context (or AppArmor profile, or SMACK label) will be inherited if that’s in use; etc.

Answered By: u1686_grawity
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.