tcgetpgrp function in C - 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);

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.)

print to an other session's controlling terminal

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?

Cloning command `script` and PTY background job problems: terminal messed up

I'm trying to recode the UNIX command script (as it is on OSX). This is part of an exercise for school to help students learn UNIX APIs. We are only allowed to use system calls, more specifically, only those available on MAN(2) pages on Mac OSX (since that's our OS at school).
I have a 'first version' that kind of works. Running a program such as ls prints the right output to the screen and in an output file.
The problem scenario
I run bash from within the script-clone. First issue is I get the following error:
bash: no job control in this shell
I have tried forcing the bash process into foreground with setpgrp and setpgid but that din't change anything so I concluded that was not the problem.
I also tried to understand why the real script command uses cfmakeraw (at least on Linux), as seen here, but I don't get it. The MAN page is not very helpful.
The real script also dup2s STDIN on the slave, as seen here, but when I do that, it seems like input isn't read anymore.
However, the bash still runs, and I can execute commands inside of it.
But if I run vim inside it, and then hit Ctrl-Z to put vim to the background, the terminal is messed up (which does not happen when I'm in my regular terminal).
So I guess I must have done something wrong. I'd appreciate any advice/help.
Here's the source code:
https://github.com/conradkleinespel/unix-command-script/tree/2587b07e7a36dc74bf6dff0e82c9fdd33cb40411
You can compile by doing: make (it builds on OSX 10.9, hopefully on Linux as well)
And run by doing: ./ft_script
Don't know it it makes more sense to have all the source code in StackOverflow as it would crowd the page with it. If needed, I can replace the Git link with the source.
I don't use OS X, so I can't directly test your code, but I'm currently writing a toy terminal emulator and had similar troubles.
about "bash: no job control in this shell"
In order to perform job control, a shell needs to be a session leader and the controlling process of its terminal. By default, your program inherits the controlling terminal of your own shell which runs your script program and which is also a session leader. Here is how to make your new slave process a session leader after fork:
/* we don't need the inherited master fd */
close(master);
/* discard the previous controlling tty */
ioctl(0, TIOCNOTTY, 0);
/* replace existing stdin/out/err with the slave pts */
dup2(slave, 0);
dup2(slave, 1);
dup2(slave, 2);
/* discard the extra file descriptor for the slave pts */
close(slave);
/* make the pts our controlling terminal */
ioctl(0, TIOCSCTTY, 0);
/* make a new session */
setsid()
At this point, the forked process has stdin/out/err bound to the new pts, the pts became its controlling terminal, and the process is a session leader. The job control should now work.
about raw tty
When you run a program inside a normal terminal, it looks like this:
(term emulator, master side) <=> /dev/pts/42 <=> (program, slave side)
If you press ^Z, the terminal emulator will write the ascii character 0x1A to the pts. It is a control character, so it won't be sent to the program, but instead the kernel will issue SIGSTP to the program and suspend it. The process of transforming characters into something else is called "line cooking" and has various settings that can be adjusted for each tty.
Now let's look at the situation with script:
term emulator <=> /dev/pts/42 <=> script <=> /dev/pts/43 <=> program
With normal line settings, what happens when you press ^Z? It will be transformed into SIGSTP by /dev/pts/42 and script will be suspended. But that's not what we want, instead we'd like the 0x1A character produced by our ^Z to go as-is through /dev/pts/42, then be passed by script to /dev/pts/43 and only then be transformed into SIGSTP to suspend the program.
This is the reason why the pts between your terminal and script must be configured as "raw", so that all control characters reach the pts between script and the program, as if you were directly working with it.

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 to Obtain Pseudo Terminal Master file descriptor from inside ssh session?

in C or bash,
I was wondering how, if possible, do you obtain from inside an ssh session, the file descriptor to the pseudo terminal master responsible for getting input to that's session's slave(pts).
The shell process has no master file descriptor, only slave.
The shell's parent process (be it sshd or xterm or screen or whatever) creates a new master by calling getpt(3) or posix_openpt(3). The function returns the master file descriptor. The parent process then obtains the slave file descriptor by calling a combination of grantpt(3), unlockpt(3), ptsname(3) and open(2). This is for Linux and other POSIXized systems, other *nixes may use other functions, but the net result is the same. The parent process has the master/slave pair of file descriptors.
The slave descriptor, and the slave descriptor only, is then passed to the shell as its standard input, output and error.
From Solaris 5.8 PTS(7D) Man-page - STREAMS pseudo-tty slave driver
Only one open is allowed on a master device.
I guess that answers my question :)
EDIT: actually it does not, because if there is a way to obtain the file descriptor, i won't need to open again, it is a file descriptor lol , no need to open
On unix-based systems, you can open the controlling terminal of the current process by opening /dev/tty. In many cases your program will already have this open as stdin, stdout and stderr, but even if your program is being invoked with stdin, stdout or stderr redirected, /dev/tty will give you the controlling terminal of the process.

Resources