print to an other session's controlling terminal - c

Why does echo hello > /dev/pts/xxx work (here xxx refers to another session's controlling terminal)?
With default setting, a process of the background process group of this session will get a signal SIGTTOU when it tries to write to stdout (here stdout refers to the controlling terminal), because the terminal driver will check whether this process belongs to the foreground process group.
So how does the terminal driver tolerate an output from another session's process? What happened there?

Related

Why does vim crash when it becomes an orphan process?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int pid = fork();
if (pid) {
sleep(5);
// wait(NULL); // works fine when waited for it.
} else {
execlp("vim", "vim", (char *)NULL);
}
}
When I run this code, vim runs normally then crashes after the 5 seconds (i.e. when its parent exits). When I wait for it (i.e. not letting it become an orphan process), the code works totally fine.
Why does becoming an orphan process become a problem here? Is it something specific to vim?
Why is this even a thing that's visible to vim? I thought that only the parent knows when its children die. But here, I see that somehow, the child notices when it gets adopted, something happens and crashes somehow. Do the children processes get notified when their parent dies as well?
When I run this code, I get this output after the crash:
Vim: Error reading input, exiting...
Vim: preserving files...
Vim: Finished.
This actually happens because of the shell that is executing the binary that forks Vim!
When the shell runs a foreground command, it creates a new process group and makes it the foreground process group of the terminal attached to the shell. In bash 5.0, you can find the code that transfers this responsibility in give_terminal_to(), which uses tcsetpgrp() to set the foreground process group.
It is necessary to set the foreground process group of a terminal correctly, so that the program running in foreground can get signals from the terminal (for example, Ctrl+C sending an interrupt signal, Ctrl+Z sending a terminal stop signal to suspend the process) and also change terminal settings in ways that full-screen programs such as Vim typically do. (The subject of foreground process group is a bit out of scope for this question, just mentioning it here since it plays part in the response.)
When the process (more precisely, the pipeline) executed by the shell terminates, the shell will take back the foreground process group, using the same give_terminal_to() code by calling it with the shell's process group.
This is usually fine, because at the time the executed pipeline is finished, there's usually no process left on that process group, or if there are any, they typically don't hold on to the terminal (for example, if you're launching a background daemon from the shell, the daemon will typically close the stdin/stdout/stderr streams to relinquish access to the terminal.)
But that's not really the case with the setup you proposed, where Vim is still attached to the terminal and part of the foreground process group. When the parent process exits, the shell assumes the pipeline is finished and it will set the foreground process group back to itself, "stealing" it from the former foreground process group which is where Vim is. Consequently, the next time Vim tries to read from the terminal, the read will fail and Vim will exit with the message you reported.
One way to see by yourself that the parent processing exiting does not affect Vim by itself is running it through strace. For example, with the following command (assuming ./vim-launcher is your binary):
$ strace -f -o /tmp/vim-launcher.strace ./vim-launcher
Since strace is running with the -f option to follow forks, it will also start tracing Vim when it's launched. The shell will be executing strace (not vim-launcher), so its foreground pipeline will only end when strace stops running. And strace will not stop running until Vim exits. Vim will work just fine past the 5 seconds, even though it's been reparented to init.
There also used to be an fghack tool, part of daemontools, that accomplished the same task of blocking until all forked children would exit. It would accomplish that by creating a new pipe and have the pipe inherited by the process it spawned, in a way that would get automatically inherited by all other forked children. That way, it could block until all copies of that pipe file descriptor were closed, which typically only happens when all processes exit (unless a background process goes out of its way to close all inherited file descriptors, but that's essentially stating that they don't want to be tracked, and they would most probably have relinquished their access to the terminal by that point.)

tcgetpgrp function in C

Syntax:
pid_t tcgetpgrp(int fd);
In MAN Page:
The function tcgetpgrp() returns the process group ID of the foreground process group on the terminal associated to fd, which must be the controlling terminal of the calling process.`
So, using this function we can get the foreground process of terminal. But I didn't understand which file descriptor is passed to this function. What is the use of file descriptor passed to this function and why?
The Open Group Base Specifications Issue 6
IEEE Std 1003.1, 2004 Edition says :
11.1.2 Process Groups
A terminal may have a foreground process group associated with it.
This foreground process group plays a special role in handling
signal-generating input characters, as discussed in Special
Characters.
tcgetpgrp is the function that can give back the ID of this group attached to a given terminal. The parameter must be a file descriptor associated to a terminal, more than this it must be a descriptor of the controlling terminal of the process :
11.1.3 The Controlling Terminal
A terminal may belong to a process as its controlling terminal. Each
process of a session that has a controlling terminal has the same
controlling terminal.
In short, a controlling terminal is the object that lets you manage jobs in your shell : dispatch CTRL-Z to suspend a job, make a job the foreground one, cancel an job with CTRL-C, etc. A controlling terminal lets you control group of processes attached to this terminal. This control may include : concurrent access to the terminal, session management, foreground/background, etc.
ctermid may give you the path of your controlling terminal (tty command line does the same). Be aware that the controlling terminal may not be the same as the terminal on which you make your standard I/Os, but in general it is the same. You can then (very commonly) use STDIN_FILENO (and the two others as well). You can also use isatty to determine if a file descriptor is associated to a terminal.
You can just call it like this:
#include <unistd.h>
pid_t pid = tcgetpgrp(STDIN_FILENO);

Writing a C program to move a process to background

I am trying to write a program , which does a fork and exec a child process and executes it in the back ground .
One approach I would see is to redirect the output to /dev/NULL file and come back to my main program . Any other ideas ?
After a process is started, shell has no more control on process file descriptors so you can not silence it by a shell command i.e. terminal has its stdin, stdout and stderr bound to the terminal and you cannot do anything about it without re-gaining control over that terminal.
There is a tool called retty how you can use it can be seen at this link retty this tool is used to attach processes running on terminals
Beside you can also use the built in disown command to disown the process which will prevent from sending a SIGHUP signal to the program when the shell exits
This link can be helpful Link to a similar problem

Terminal Access Control issues

I am attempting to write a shell. When a foreground process is run, the forked process pipeline is given its own process group id. The terminal is then given over to this process group id (using tcsetpgrp) and the shell waits for it to terminate before giving itself terminal control once again. This works perfectly fine.
The issue that arises is when I attempt to run a background process. Again, I give all of the processes in the pipeline a single process group id but this time I do not give terminal control to this group. Upon running, the output of a given background command is output to the terminal (before it is finished executing) and the terminal gives the user back the prompt at the same time. What should have happened is that the child process that attempts to write to the terminal should get a SIGTTOU and it should stop, but this clearly doesn't happen. I verified that the forked processes all have the same process group id and that this id is different from the shell's.
Upon exiting the shell (via ctrl-c) and returning to the standard bash shell that ran it, because I did not reap the background process upon shell termination, the background process continues running (which is excepted). What is weird though is that this process continues writing output to the bash shell even though it is not the foreground process. This leads me to conclude that either this background process is not getting any SIGTTOUs because of a POSIX bug (unlikely), it is handling them (causing the default action of stopping to be ignored), or the background process is ignoring SIGTTOUs.
Is there a way to, before exec'ing a forked process, ensure that it will stop upon receiving a SIGTTOU (assuming that the exec binary does not change anything)?
SIGTTOU is sent to a background process which tries to write to the terminal only if the termios flag TOSTOP is set for that terminal. By default, it is generally not set, in which case the background process can happily write to the terminal. (The TOSTOP flag does not affect read permissions. If the process tries to read, it will be sent a SIGTTIN.)
So, yes, there is something the foreground process can do: use tcsetattr to set TOSTOP
The solution was to make the forked process execute the following before calling exec:
struct termios term;
if (tcgetattr(STDIN_FILENO, &term) < 0)
printf("ERROR\n");
term.c_lflag = TOSTOP;
if (tcsetattr(STDIN_FILENO,TCSANOW,&term)<0)
printf("ERROR\n");

How do I know if an C program's executable is run in foreground or background?

In my C program I want to know if my executable is run in foreground like this
$./a.out
or like this
$./a.out &
If you are the foreground job,
getpgrp() == tcgetpgrp(STDOUT_FILENO)
or STDIN_FILENO or STDERR_FILENO or whichever file descriptor you're attached to your controlling terminal by. (If you're not sure, open("/dev/tty") will always get you a file descriptor to your controlling terminal, if one exists.)
This is what openssh does, and is a bit easier than handling SIGTTIN/SIGTTOU if you just want a quick check.
On the other hand, you may have been backgrounded
$ ./a.out
^Z
[1]+ Stopped ./a.out
$ bg
[1]+ ./a.out &
or foregrounded
$ fg
./a.out
at any point in time. You cannot expect that you can check this once and it will still be true (or false) later.
From the Bash Reference Manual: Job Control Basics:
Background processes are those whose process group id differs from the terminal's; such processes are immune to keyboard-generated signals. Only foreground processes are allowed to read from or write to the terminal. Background processes which attempt to read from (write to) the terminal are sent a SIGTTIN (SIGTTOU) signal by the terminal driver, which, unless caught, suspends the process.
So the solution is to install a signal handler for SIGTTIN and then try to read from stdin (turn buffering off or it will block). If you get "0 bytes read" back, then you're running in the foreground.
[EDIT] Note that the status of a process can change. You can use the job control commands of the shell (Ctrl-Z, bg, fg and jobs) to do this.
To my knowledge this is not possible and usually not necessary either.
Please explain why you want to do this.
[invalid]IIRC, getppid() (on *nix systems) will give you the parent id. if it is 0, the 'console' is your parent and so you are running in the background.
[/invalid]
[edit]
int devtty;
if ((devtty = open ("/dev/tty", O_RDWR)) < 0)
printf ("daemon\n");
note that this is only valid on *nix systems (and then only if nobody has deleted /dev/tty -- for whatever reason)
[/edit]
There may be a possibility that you have more than one process
running in the background:
$ jobs
[1] Stopped teamviewer
[2]- Stopped vim
[3]+ Stopped firefox
use: fg %2 to send the vim process back to foreground.
To send the last process back to foreground simply use: fg with no
arguments.
You can also type % process_name to resume the stopped process.
To suspend the process running in the background, use:
kill -19 %job_id.
The -19 signal is SIGSTOP (the signal sent by Ctrl - Z) .
you can always see the list by typing kill -l
Moving jobs between background / foreground:
If you have already typed a command and forgot to use the &, you can put a foreground job into the background by typing ^Z (CTRL-Z) to suspend the job, followed by bg, to put it into the background:
$ sleep 99
^Z
[1]+ Stopped sleep 99
$ bg
[1]+ sleep 99 &
You can list the jobs of the current shell using the jobs command.
Just remember that "exiting shell" affects jobs as well:
Jobs running in the background when the shell exits are left running.
Jobs that are paused (“Stopped”) when the shell exits are terminated.
Sending signals to jobs and processes
You can send signals, including termination signals, to jobs that are started from the current shell using job numbers using %(JOBID) instead of process numbers(PID):
$ kill %1
[1]+ Terminated sleep 99
To send signals to processes or jobs that are not started from the current shell, you first need to use ps to find their process numbers(PID).
You can refer to this link:
processes and jobs
The general job control commands in Linux are:
jobs - list the current jobs
fg - resume the job that's next in the queue
fg %[number] - resume job [number]
bg - Push the next job in the queue into the background
bg %[number] - Push the job [number] into the background
kill %[number] - Kill the job numbered [number]
kill -[signal] %[number] - Send the signal [signal] to job number [number]
disown %[number] - disown the process(no more terminal will be owner), so command will be alive even after closing the terminal.
That's pretty much all of them. Note the % infront of the job number in the commands - this is what tells kill you're talking about jobs and not processes.

Resources